迭代器
什么是迭代
迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。
在python中,迭代是访问集合元素的一种方式。对list、tuple、str等类型的数据使用for…in…的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。
什么是可迭代对象
一个对象如果实现了__iter__方法(魔法方法),那么我们称这个对象是可迭代对象。
或者可以这样理解,只要是可以通过for…in…的形式进行遍历的,那么这个数据类型就是可以迭代的。
判断数据是否可迭代
python中要判断一个对象是否是可迭代的,可以用collections模块中的Iterable来进行判断。isinstance函数会返回True或False来表明对象是(True)或者不是(False)可迭代对象。
from collections.abc import Iterable print(isinstance(["abc"], Iterable)) print(isinstance([100], Iterable))
什么是迭代器
迭代器的作用是用来访问容器(用来保存元素的数据结构)中的元素,所以使用迭代器,我们就可以访问容器中里面的元素。
迭代器是一个可以记住遍历的位置的对象。迭代器对象从第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器的本质
我们可以通过iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据。
使用迭代器取数据
from collections.abc import Iterator nums = [3, 6, 8] # 可迭代对象 nums = iter(nums) # 创建了迭代器 print("nums", isinstance(nums, Iterator)) # 判断是否是迭代器 # 取出迭代器的数据 num1 = next(nums) print(num1) num1 = next(nums) print(num1) num1 = next(nums) print(num1)
若迭代的次数超过了可迭代对象的长度, 就会报StopIteration异常。
自定义迭代器
使用__iter__和__next__方法自定义迭代器
只要在类中,定义__iter__方法,那么这个类创建出来的对象一定是可迭代对象
如果类中实现了__iter__方法和__next__方法的对象,就是迭代器。
当我们调用iter()函数提取一个可迭代对象的 迭代器时,实际上会自动调用这个对象的__iter__方法,并且这个方法返回迭代器。
# 使用迭代器完成学生管理系统 class StuSystem(object): """ 学生管理系统 """ def __init__(self): self.stus = [] self.current_num = 0 def add(self): """ 添加一个新的学生 :return: """ name = input("请输入新学生的姓名:") tel = input("请输入新学生的手机号:") address = input("请输入新学生的住址:") new_stu = dict() new_stu["name"] = name new_stu["tel"] = tel new_stu["address"] = address self.stus.append(new_stu) def __iter__(self): return self def __next__(self): if self.current_num < len(self.stus): ret = self.stus[self.current_num] self.current_num += 1 return ret else: self.current_num = 0 raise StopIteration # 创建对象 stu = StuSystem() # 添加信息 stu.add() stu.add() stu.add() stus = [x for x in stu] print(stus)
凡是可作用于for循环的对象都是Iterable 类型;
凡是可作用于 next() 函数的对象都是Iterator 类型;
集合数据类型如list 、dict、str等是 Iterable但不是Iterator,不过可以通过 iter()函数获得一个Iterator对象。
生成器
一边循环一边计算的机制,称为生成器(generator)
在Python中,使用了yield的函数被称为生成器。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作。生成器函数一般是通过for循环调用,for循环自带next方法。
创建生产器的方法
1.将列表推导式的[]改为()
2.在函数中使用yield关键字,函数就变成了一个generator
使用生成器完成斐波那契数列
def fib_generator(): num1 = 1 num2 = 1 while True: temp_num = num1 num1, num2 = num2, num1+num2 # return temp_num # 方式1代码 yield temp_num # 方式1代码(方式1不能够生成1,1,2,3,5...斐波那契数列) # print(fib_generator()) # print(fib_generator()) # print(fib_generator()) # print(fib_generator()) # 方式2代码(可以生成斐波那契数列) fib = fib_generator() print(next(fib)) print(next(fib)) print(next(fib)) print(next(fib))
关键字yield
yield是一个类似return的关键字。当我们调用这个函数的时候并不是返回计算的结果。而是返回一个生成器。只有迭代这个生成器的时候才会计算结果。
next和send
next()方法:
在调用生成器运行的过程中,每次遇到 yield ,函数返回当前的值,并且会暂停并保存当前所有的运行信息, 并在下一次执行 next() 方法时从当前位置继续运行。
send()方法:
先理解个概念【休眠】:意思就是暂时保留先不进行,等待需要时再进行。作用与next()作用相似
区别:
1.send(value)可以传递value给yield,即:我们可以指定yield返回啥就返回啥,
2.next()不能传递特定的值,只能传递None进去。
def genterator_test(): while True: print("--1-") num = yield 100 print("--2--", "num=", num) g = genterator_test() print(g.send(None)) print(g.send(11)) print(g.send(22))
生成器的特点:
存储的是生成数据的方式(即算法),而不是存储生成的数据,因此节约内存。
装饰器
装饰器是给现有的模块增添新的小功能,可以对原函数进行功能扩展,而且还不需要修改原函数的内容,也不需要修改原函数的调用。
装饰器的功能
装饰器的实现是由闭包支撑的;
装饰器本质上是⼀个python函数,它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能;
装饰器的返回值也是⼀个函数的对象,它经常用于有切面需求的场景,实现路由传参,flask的路由传参依赖于装饰器,浏览器通过url访问到装饰器的路由,从而访问视图函数获得返回的HTML页面;
定义装饰器
def check_login(func): def inner(): # 验证1 # 验证2 # 验证3 func() return inner @check_login def f1(): print('f1')
def check_login(func): def inner(): # 验证1 if "admin" != input("请输入用户名:"): return "用户名不正确" # 验证2 if "123456" != input("请输入密码:"): return "密码不正确" # 验证3 if "7788" != input("请输入手机短信验证码:"): return "验证码不正确" func() return inner @check_login def f1(): print('f1') f1() # 调用f1函数
闭包
什么是闭包
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).
# 定义函数可以理解为: # 定义了一个全局变量,其变量名字是函数的名字,即test # 这个test变量指向了一个代码块,这个代码块是函数 # 其实就是说test保存了一个代码块的地址,即引用 def test(): print("--- in test func----") test() # 这是调用函数 ret = test # 用另外一个变量 复制了 test这个引用,导致ret变量也指向那个 函数代码块 # 下面输出的2个地址信息是相同的 print(id(ret)) print(id(test)) # 通过引用调用函数 ret()
函数、匿名函数、闭包、对象 当做实参时的区别
匿名函数能够完成基本的简单功能,,,传递是这个函数的引用 只有功能
普通函数能够完成较为复杂的功能,,,传递是这个函数的引用 只有功能
闭包能够将较为复杂的功能,,,传递是这个闭包中的函数以及数据,因此传递是功能+数据
对象能够完成最为复杂的功能,,,传递是很多数据+很多功能,因此传递是功能+数据