一、迭代器
1. 什么是迭代器
迭代器,顾名思义,就是重复,循环。比如for循环,是对列表序列和字典进行迭代,但也可能对其他的对象进行迭代,这里要说的是__iter__方法这个对象,
这个方法是迭代器必须要有的。
2. 如何创建迭代器
迭代器里有两个方法,一个是__iter__,一个是__next__。迭代器不是把序列或者字典全部放到一个容器里,而是通过计算的方法,每次计算返回一次,下一次计算结果是在上一次计算结果的基础上进行返回的,这样可以节省内存占用。
那么迭代器就是具有__next__方法的对象,当调用__next__方法时,就会计算并返回下一个值。如果没有值返回,那么就抛出StopIteration异常。
class MyIterator:
def __iter__(self):
self.a = 1
return self
def __next__(self):
self.a += 1
return self.a
obj = MyIterator()
x = iter(obj) # iter后面可以跟一个可迭代对象obj
print(x.__next__())
print(x.__next__())
print(x.__next__())
二、生成器
1. 什么是生成器
生成器的概念是基于函数,是一种用函数语法定义的迭代器。
generator object里也有__next__方法,它的本质其实也是一个迭代器,是从迭代器中衍生出来的一个方法,是一种特殊的迭代器。
函数yield是生成器,yield会return结果,但是yield之后如果没有next指令,那么程序就会暂时停止,冻结,等待被激活,如果有__next__,那么就激活,往后继续执行,不会结束。
2. 如何创建生成器
def func():
x = 1
while x < 10:
print("看看yield是怎么运作的:" + str(x))
yield x
x += 1
print("这是一个生成器:" + str(x))
obj = func()
print(obj)
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
3. 生成器的实现过程
以上述代码为例,它的运行过程是这样的:
【由于是md编辑,下文中__next__()自动转成了next(),问题不大】
- 因为程序func里有yield,那么func本身就是一个generator object,所以第一次打印print(obj)是为了证明它是一个生成器对象;
- 第一个print(obj.next()),程序进入while循环体,print(“看看yield是怎么运作的:” + str(x)),遇到yield后,就return x,此时程序已经冻结;
- 第二个print(obj.next()),程序收到指令后,从之前冻结的位置开始激活,继续按下面顺序执行:
a. x赋值操作 x += 1
b. 打印print(“这是一个生成器:” + str(x))
c. 打印print(“看看yield是怎么运作的:” + str(x))
d. 直到又遇到yield,return x,此时程序继续冻结,等待新的next指令。 - 第三个print(obj.next()),和步骤3是一样的过程,这里不再赘述。
三、装饰器
1. 什么是闭包
在讲装饰器之前,先说下什么是闭包吧。
先来一串代码引入闭包背景= =
比如写了一个append的功能:
x = []
def func():
x.append("闭包")
print(x)
return x
if __name__ == '__main__':
func()
func()
func()
这个功能在本地模块调用是没有任何问题的,但如果其他人直接调用你的模块,并且重新给x赋了初始值,那么这个模块就会出现bug。
from xxx import bibao_1
bibao_1.x = ["bug"]
bibao_1.func()
bibao_1.func()
bibao_1.func()
因为函数有外部变量,是可以改变的,代码设计有安全漏洞,不严谨。那么我们可以把变量写到里面,但是写到里面又有一个问题,x会重新赋值,多次调用起不到累加的功能了。
def func():
x = [] # 把变量写到里面,每次调用,x 会重新赋值
x.append("闭包")
print(x)
return x
if __name__ == '__main__':
func()
func()
func()
这时我们可以在外部再加一层函数,x就变成了内部变量,无法改变。这样问题就得到了解决。
def func_new():
x = []
def func():
x.append("闭包")
print(x)
return x
return func
if __name__ == '__main__':
obj = func_new()
obj()
obj()
obj()
那么这个解决问题的过程,就叫闭包。
闭包是一个函数,它的内部有一个函数变量(这个变量也叫自由变量)和一个内嵌函数(这个内嵌函数必须引用外部嵌套函数的变量)并且返回内嵌函数的对象。
闭包的作用是锁定函数的变量,别人无法改变。
2. 什么是装饰器
知道了闭包,那么理解装饰器就不难了。
装饰器是闭包演变过来的,也是闭包实现的一种方式。因此它本质上也是一个函数,它可以让已经存在的其他函数在不需要做任何代码改动的情况下增加额外的功能,其返回的值就是一个函数对象。
通俗的讲,就好比给你自己买块欧米伽,手表并不影响你这个人物(对象)本身,但可以随时看时间,也好看(增加额外的功能)。
装饰器的用途较广,日志,性能测试脚本,缓存,权限校验,处理事务等等。
3. 装饰器的实现过程
def fun():
print("hello fun")
return "xxxx"
def fun2():
print("hello fun2")
return "yyyy"
def func(fun):
def inner():
print("前置:进入内嵌函数")
f = fun() # 调用内部函数
print("后置:内嵌函数执行完成")
return f # 返回内部函数的结果
return inner # 返回内嵌函数inner的对象
if __name__ == '__main__':
obj1 = func(fun) # 实例化内嵌函数inner的对象
# print(obj) # 打印内嵌函数inner的对象
# obj() # 调用inner,返回内嵌函数inner的对象
print(obj1())
obj2 = func(fun2)
print(obj2())
如上我们可以看到函数fun和函数fun2都可以作为参数传到func,我们不需要去改变func的代码。这里的func就是一个装饰器,它的参数需要传另外一个函数,在调用fun和fun2之前,先调用func的一个动作。这个就是装饰器的实现过程。
4. 如何创建装饰器
装饰器是带有语法糖的函数,效果和上述一样,优点是结构简单,清晰明了。
我们还是用上面的装饰器进行结构的优化。
def func(fun):
def inner():
print("前置:进入内嵌函数")
f = fun() # 调用内部函数
print("后置:内嵌函数执行完成")
return f # 返回内部函数的结果
return inner # 返回内嵌函数inner的对象
@func # 语法糖
def fun():
print("hello fun")
return "xxxx"
@func
def fun2():
print("hello fun2")
return "yyyy"
fun()
fun2()
如果执行函数有变量怎么办?
那就修改装饰器内部函数,加上万能传参*args,**kwargs,就可以接收任意参数。
def func(fun):
def inner(*args, **kwargs):
print("前置:进入内嵌函数")
f = fun(*args, **kwargs) # 调用内部函数
print("后置:内嵌函数执行完成")
return f # 返回内部函数的结果
return inner # 返回内嵌函数inner的对象
@func # 语法糖
def fun(a=1, b="test", c=None):
if c is None:
c = {"name": "test", "age": 18}
print(a, b, c)
print("hello fun")
return "xxxx"
@func
def fun2():
print("hello fun2")
return "yyyy"
fun()
fun2()
fun(a={"name": "jack", "age": 20}, b=4, c=100)