装饰器,迭代器与生成器
闭包
定义一个函数,在函数的内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包.
def line_conf(a, b):
def line(x):
return a * x + b
return line
line1 = line_conf(1, 5)
line2 = line_conf(3, 5)
print(line1(2))
print(line2(3))
如上所示,函数line与变量a,b构成了闭包.在创建闭包的过程中,通过lin_conf的参数进行取值,在一定程度上提高了代码的可复用性.
但是由于外包引用了外部函数的局部变量,使得外部函数的的局部变量没有及时的释放,消耗了内存.
装饰器
函数的调用
def foo():
print("foo")
foo() # 表示执行foo 函数
foo # 表示函数
# =======================================
def too():
print("too")
too = lambda x: x + 1
too() # 执行lambda 表达式
函数名只是一个变量,只是指向了定义的函数,所以才能通过函数名()的方式进行调用,如果函数名=XXX被修改了,那么当在执行函数名()的时候,调用的就不是之前的函数.
装饰器的作用
一般而言,写代码的过程中应当遵循开放封闭的原则,即,在编程的过程当中,已经实现的功能代码不允许被修改,但可以被扩展.
在python中通常使用装饰器完成代码功能的扩展,一般用于以下场景:
- 引入日志
- 函数执行时间统计
- 执行函数前的预备处理
- 执行函数后的清理功能
- 权限校验
- 缓存
装饰器示例
装饰器格式如下:
def wrapper(func):
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
带参数的装饰器
from time import ctime, sleep
def timefun_arg(pre="hello"):
def timefun(func):
def wrapped_func():
print("%s called at %s %s" % (func.__name__, ctime(), pre))
return func()
return wrapped_func
return timefun
@timefun_arg("itcast")
def foo():
print("I am foo")
# 装饰过程如下
# 1. 调用timefun_arg("itcast")
# 2. 将步骤1得到的返回值,即time_fun返回, 然后time_fun(foo)
# 3. 将time_fun(foo)的结果返回,即wrapped_func
# 4. 让foo = wrapped_fun,即foo现在指向wrapped_func
foo()
# output
# foo called at Wed Sep 23 13:57:27 2020 itcast
# I am foo
# foo called at Wed Sep 23 13:57:29 2020 itcast
# I am foo
迭代器
迭代
迭代是访问集合元素的一种方式,迭代器是一个可以记住遍历的位置的对象,迭代器对象是从集合的第一个元素开始访问,知道所有的元素被访问结束,迭代器只能往前不能往回退.
可迭代对象
可以通过for...in...这类语句迭代读取一条数据供我们使用的对象,称之为可迭代对象iterable
可通过isinstance()判断一个对象是否是Iterable对象.
from collections import Iterable
isinstance([],Iterable)
Out[12]: True
isinstance((),Iterable)
Out[13]: True
isinstance({},Iterable)
Out[14]: True
isinstance(str,Iterable)
Out[15]: False
isinstance('abcd',Iterable)
Out[1]: True
isinstance(100,Iterable)
Out[17]: False
可迭代对象的本质
可被迭代要满足的要求就叫做可迭代协议,内部实现了__iter__方法.
可迭代对象通过内部实现了一个__iter__方法向我们提供一个迭代器,我们在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器以此获取对象中的每一个数据.
即,一个具备了__iter__方法的对象,就是一个可迭代对象.
from collections import Iterable
class MyClass(object):
def __init__(self):
self.container = []
def __iter__(self):
#
pass
def __add__(self, item):
self.container.append(item)
myclass = MyClass()
print(isinstance(myclass, Iterable))
>>> True
iter()与next()函数
list,tuple都是可迭代对象,我们可以通过iter()方法获取这个可迭代对象的迭代器,然后可以对获取到的迭代器不断使用next()函数获取下一条数据.
li = ['aa','bb','cc','dd']
li_iter = iter(li)
next(li_iter)
Out[20]: 'aa'
next(li_iter)
Out[21]: 'bb'
next(li_iter)
Out[22]: 'cc'
next(li_iter)
Out[23]: 'dd'
next(li_iter)
Traceback (most recent call last):
File "<ipython-input-24-ab6a83f394a1>", line 1, in <module>
StopIteration
当我们他已经迭代万最后一个数据之后,再次调用next()函数会抛出一个异常,来告诉我们所有的数据已经迭代完成,不能在执行next()函数了.
迭代器
可以使用isinstance()判断一个对象是否是迭代器Iterator对象.
from collections import Iterator
isinstance([],Iterator)
Out[3]: False
isinstance(iter([]),Iterator)
Out[4]: True
由上可知,迭代器是用来记录每次迭代访问的位置,在我们对迭代器对象使用next()方法的时候,迭代器会返回他所记录的位置的下一个位置的数据,实际上,在我们使用next()方法的饿时候,调用的就是迭代器对象的__next__方法,因此我们如果想构建一个迭代器,就要实现他的__next__方法.
但是Python要求迭代器本身也是可迭代的,所以我们还要为他实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身就是一个迭代器,所以__iter__方法返回自身就可以.
class MyClass(object):
def __init__(self):
self.container = []
def __iter__(self):
# 构建返回的迭代器对象
myiterator = MyIterator(self)
return myiterator
def add(self, item):
self.container.append(item)
class MyIterator(object):
"""自定义的可供可迭代对象使用的一个迭代器"""
def __init__(self, mylist):
self.mylist = mylist
# 用来记录当前访问位置
self.current = 0
def __iter__(self):
# 返回自身
return self
def __next__(self):
# 构建迭代器自身的next()方法
if self.current < len(self.mylist.container):
item = self.mylist.container[self.current]
self.current += 1
return item
else:
raise StopIteration
if __name__ == '__main__':
my_list = MyClass()
my_list.add(1)
my_list.add(2)
my_list.add(3)
my_list.add(4)
my_list.add(5)
for item in my_list:
print(item)
for循环的本质
for item in Iterable循环的本质就是先通过iter()方法获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断地调用next()方法,来获取下一个值,并将它赋值给item,当遇到StopIteration的时候循环结束
迭代器的应用场景
迭代器最核心的功能就是通过next()函数的调用来返回下一个数据值,可见下例:
class FibIterator(object):
"""斐波那契数列迭代器"""
def __init__(self, n):
"""
:param n: 指明生成数列前n个数
"""
self.n = n
# 记录当前生成数列中的第几个数
self.current = 0
# 记录前前一个数的位置
self.num1 = 0
# 记录前一个数的位置
self.num2 = 1
def __next__(self):
"""被next()方法调用"""
if self.current < self.n:
num = self.num1
self.num1, self.num2 = self.num2, self.num1 + self.num2
self.current += 1
return num
else:
raise StopIteration
def __iter__(self):
"""迭代器的__iter__返回自身即可"""
return self
if __name__ == '__main__':
fib = FibIterator(10)
for num in fib:
print(num, end=" ")
接收可迭代对象的容器
除了for循环之外,list与tuple都可以接收可迭代对象
li = list(FibIterator(10))
print(li)
tup = tuple(FibIterator(10))
print((tup))
生成器
在迭代器中,我们每次获得数据(next())时都按照特定的规律进行生成.但是在我们实现一个迭代器的时候,关于当前迭代的状态需要我们自己进行记录,进而才能依据当前状态生成下一个数据.为了纪录当前的状态,并配合next()函数进行使用,我们采用更为便捷的生成器语法,即generator.
因此,生成器是一种特殊的装饰器语法
创建生成器的语法
把列表生成式的[]修改为()
li = [ x**2 for x in range(10) ]
li
Out[6]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
gi = ( x**2 for x in range(10))
list(gi)
Out[8]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
如上,li是一个列表,gi是一个生成器,对于生成器可以使用for,next()函数或者list()等方法进行使用.
使用函数实现
使用函数实现复杂的生成器功能的时候,我们可以使用yield代替return,此时的的函数就是一个生成器.
也可以认为,只要在函数中存在关键字yield,那么这个函数就不再是一个函数而是一个生成器.
def fib(n):
current = 0
num1, num2 = 0, 1
while current < n:
num = num1
num1, num2 = num2, num1 + num2
current += 1
yield num
return 'done'
if __name__ == '__main__':
fi = fib(10)
print(list(fi))
>>> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
yield关键字的作用
- 保存当前的运行状态,然后暂停执行,即将生成器(函数)挂起,
- 将
yield关键字后面的表达式的值作为返回值返回,此处视线的作用相当于return - 再次使用
next()函数让生成器从断点处开始执行,即唤醒生成器函数
使用send唤醒
除了使用next()函数可以将生成器唤醒,还可以使用send()函数来唤醒执行.
使用send()函数的时候,可以在唤醒函数的同时想断点处传入一个附加数据
def gen():
...: i = 0
...: while i<5:
...: temp = yield i
...: print(temp)
...: i+=1
...:
f = gen()
next(f)
Out[26]: 0
next(f)
None
Out[27]: 1
f.send('hahahddd')
hahahddd
Out[28]: 2
f.send('hahahddd')
hahahddd
Out[29]: 3
next(f)
None
Out[30]: 4
next(f)
None

浙公网安备 33010602011771号