一、迭代器
1. 定义
对于list、string、tuple、dict等这些容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数。iter()是python内置函数。iter()函数会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内的元素。next()也是python内置函数。在没有后续元素时,next()会抛出一个StopIteration异常,通知for语句循环结束。
迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的_next_方法(Python3中是对象的_next_方法,Python2中是对象的next()方法)。所以,我们要想构造一个迭代器,就要实现它的_next_方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现_iter_方法,而_iter_方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的_iter_方法返回自身self即可
2. 一些术语
- 迭代器协议:对象需要提供next()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。
- 可迭代对象:实现了迭代器协议对象。list、tuple、dict都是Iterable(可迭代对象),但不是Iterator(迭代器对象)。但可以使用内建函数iter() ,把这些都变成Iterable(可迭代器对象)。
- for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束
# 随便定义一个list listArray=[1,2,3] # 使用iter()函数 iterName=iter(listArray) print(iterName) 结果如下:是一个列表list的迭代器 <list_iterator object at 0x0000017B0D984278> print(next(iterName)) print(next(iterName)) print(next(iterName)) # 没有迭代到下一个元素,直接抛出异常 print(next(iterName)) # 1 # 2 # 3 # Traceback (most recent call last): # File "Test07.py", line 32, in <module> # StopIteration
# 实现斐波那契数列 class Fib(object): def __init__(self, max): super(Fib, self).__init__() self.max = max def __iter__(self): self.a = 0 self.b = 1 return self def __next__(self): fib = self.a if fib > self.max: raise StopIteration self.a, self.b = self.b, self.a + self.b return fib # 定义一个main函数,循环遍历每一个菲波那切数 def main(): # 20以内的数 fib = Fib(20) for i in fib: print(i) # 测试 if __name__ == '__main__': main()
3. 可迭代对象
list,tuple,set,dict,str等可以直接作用于for循环的对象,称为可迭代对象。可迭代对象实现了__iter__方法,用于返回迭代器。
demo = [1,2,3,4] print(isinstance(demo, Iterable)) //True iter_object = iter(demo) print(iter_object) //<list_iterator object at 0x00000258DC5EF748>
4. 实现一个for循环
s = 'hello' it = iter(s) while (True): try: print(next(it)) except StopIteration: del it break
StopIteration异常将在迭代器耗尽后被抛出,for-in、生成式(comprehension)、元组解压(tuple unpacking)等迭代操作都会处理并这个异常。
迭代器是个迭代值生产工厂,它保存迭代状态,并通过next()函数产生下一个迭代值。实现迭代器需要实现以下两个方法:__iter__
返回self ;__next__
返回下一个可用的元素,如果无可用元素则抛出StopIteration异常
迭代器实现__iter__
,因此所有的迭代器都是可迭代的,下图展示了iterable和iterator的结构。
二、生成器
1. 定义
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator。
作用:延迟操作。也就是在需要的时候才产生结果,不是立即产生结果。
2. 示例
(1) 第一类
第一类:生成器函数:还是使用 def 定义函数,但是,使用yield而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
# 菲波那切数列 def Fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return '亲!没有数据了...' # 调用方法,生成出10个数来 f=Fib(10) # 使用一个循环捕获最后return 返回的值,保存在异常StopIteration的value中 while True: try: x=next(f) print("f:",x) except StopIteration as e: print("生成器最后的返回值是:",e.value) break
(2) 第二类
生成器表达式:类似于列表推导,只不过是把一对大括号[]
变换为一对小括号()
。但是,生成器表达式是按需产生一个生成器结果对象,要想拿到每一个元素,就需要循环遍历。
# 一个列表 xiaoke=[2,3,4,5] # 生成器generator,类似于list,但是是把[]改为() gen=(a for a in xiaoke) for i in gen: print(i) 结果是: 2 3 4 5
3. send()和__next__()
# send和__next__()⼀样都可以让⽣成器执⾏到下⼀个yield. def eat(): print("我吃什么啊") a = yield "馒头" print("a=",a) b = yield "烧饼" print("b=",b) c = yield "菜盒子" print("c=",c) yield "GAME OVER" gen = eat() # 获取生成器 ret1 = gen.__next__() print(ret1) ret2 = gen.send("胡辣汤") # 给上一个yield传值,也就是a print(ret2) ret3 = gen.send("狗粮") print(ret3) ret4 = gen.send("猫粮") print(ret4) 结果: 我吃什么啊 馒头 a= 胡辣汤 烧饼 b= 狗粮 菜盒子 c= 猫粮 GAME OVER
- send和next()都是让⽣成器向下走⼀次
- send可以给上⼀个yield的位置传递值, 不能给最后⼀个yield发送值. 在第⼀次执⾏⽣成器代码的时候不能使⽤send()
4. yield和return
yield是分段来执⾏⼀个 函数. return呢? 直接停⽌执⾏函数。种使⽤⽣成器. ⼀次就⼀个. ⽤多少⽣成多少. ⽣成器是⼀个⼀个的指向下⼀个. 不会回去, __next__()
到哪, 指针就指到哪⼉. 下⼀次继续获取指针指向的值.
5. 生成器迭代器
三、装饰器
1. 定义
装饰器(Decorator)从字面上理解,就是装饰对象的器件。可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出。装饰器有很多种,有函数装饰器、类装饰器。它强调开放封闭原则,装饰器语法是将@装饰名放在被装饰对象上面。简单来说,可以把装饰器理解为一个包装函数的函数,它一般将传入的函数或者是类做一定的处理,返回修改之后的对象.所以,我们能够在不修改原函数的基础上,在执行原函数前后执行别的代码
装饰器的本质是一个python函数,其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值(return)也是一个函数对象。比如:插入日志、性能测试、事务处理、缓存、权限校验。比如说,有需要功能模块需要添加认证功能;那这时就可以使用装饰器实现。
2. 定义简单装饰器
(1) 代码示例
def outer(func): def inner(): print('认证成功!') result = func() print('日志添加成功!') return result return inner @outer def f1(): print('业务部门 1 数据接口...') 结果: 认证成功! 业务部门1数据接口...... 日志添加成功
(2) 代码分析
①程序开始运行,从上往下解释,读到 def outer(func):的时候,发现是一个“一等公民”函数,于是把函数体加载到内存里,然后过 ↓
②读到@outer时,程序被@语法糖吸引住了,知道这是个装饰器,按规矩要立即执行的,于是程序开始运行@后面那个名字outer所定义的函数
③程序返回到outer函数,开始执行装饰器的语法规则。规则是:被装饰的函数的名字会被当做参数传递给装饰函数。装饰函数执行它自己内部的代码后,会将它的返回值赋值给被装饰的函数。原来的f1函数被当做参数传递给了func,而f1这个函数名之后会指向inner函数。
④注意:@outer和@outer()有区别,没有括号时,outer函数依然会被执行,这和传统的用括号才能调用函数不同,需要特别注意!另外,是f1这个函数名(而不是f1()这样被调用后)当做参数传递给装饰函数outer,也就是:func = f1,@outer等于outer(f1),实际上传递了f1的函数体,而不是执行f1后的返回值。还有,outer函数return的是inner这个函数名,而不是inner()这样被调用后的返回值。
⑤.程序开始执行outer函数内部的内容,一开始它又碰到了一个函数inner,inner函数定义块被程序观察到后不会立刻执行,而是读入内存中(这是默认规则)
⑥再往下,碰到return inner,返回值是个函数名,并且这个函数名会被赋值给f1这个被装饰的函数,也就是f1 = inner。根据前面的知识,我们知道,此时f1函数被新的函数inner覆盖了(实际上是f1这个函数名更改成指向inner这个函数名指向的函数体内存地址,f1不再指向它原来的函数体的内存地址),再往后调用f1的时候将执行inner函数内的代码,而不是先前的函数体。那么先前的函数体去哪了?还记得我们将f1当做参数传递给func这个形参么?func这个变量保存了老的函数在内存中的地址,通过它就可以执行老的函数体,你能在inner函数里看到result = func()这句代码,它就是这么干的!
3、带有参数的装饰器
def outer(func): def inner(*args,**kwargs): print("认证成功!") result = func(*args,**kwargs) print("日志添加成功") return result return inner @outer def f1(name,age): print("%s 正在连接业务部门1数据接口......"%name) # 调用方法 f1("jack",18)
4、一个函数被多个装饰器修饰
def outer1(func): def inner(*args,**kwargs): print("认证成功!") result = func(*args,**kwargs) print("日志添加成功") return result return inner def outer2(func): def inner(*args,**kwargs): print("一条欢迎信息。。。") result = func(*args,**kwargs) print("一条欢送信息。。。") return result return inner @outer1 @outer2 def f1(name,age): print("%s 正在连接业务部门1数据接口......"%name) # 调用方法 f1("jack",18) 执行结果: 认证成功! 一条欢迎信息。。。 jack 正在连接业务部门1数据接口...... 一条欢送信息。。。 日志添加成功
5. 装饰器修复功能
# 可获取func1自己的函数名,即帮助文档 from functools import wraps def timer(func): @wraps(func) def inner(): print(time.time()) ret = func() # 原来的函数 return ret return inner @timer # func1 = timer(func1) def func1(): """ func1 xxxx :return: """ print('func1') return 'func1的返回值' print(func1.__name__)