迭代器,生成器,装饰器
迭代器:iterator
可迭代对象:iterable
从表现形式上看:能使用for循环迭代的,就是可迭代对象!
(字符串,列表,字典,集合,元组.)
它们都有一个方法:__iter__,魔法方法,推荐使用iter(x)
iter(x) -> 得到的是一个绑定到当前可迭代对象身上的迭代器!!!
for循环实际上干了两件事:
1.获取可迭代对象的迭代器.
2.通过迭代器遍历可迭代对象!!!
迭代器:实现了__iter__,__next__方法的对象(也称为实现了迭代器协议.),就是迭代器.
这两个方法是魔法方法(Python中不推荐直接使用的),推荐使用替代方法:
iter(x) -> 返回的就是这个迭代器本身.
next(x) -> 返回的是下一个可用元素.
lst = [1,2,3]
# 获取到迭代器对象
i = iter(lst)
# 使用迭代器的next方法,依次获取元素
print(next(i))
print(next(i))
手动使用next方法有个缺点:没有办法很好的控制边界!!!
使用for循环可以自动判定边界.
可迭代对象和迭代器的关系:
可迭代对象每次调用iter方法,获取的都是新的迭代器.
如何验证一个对象是可迭代的?还是迭代器?
from collections import Iterator,Iterable
print(isinstance([1,2,3],Iterable)) True
print(isinstance([1,2,3],Iterator)) False
生成器:generator
一种流式生成数据的方式:
流式产生数据:保存的不是真实的数据,而是产生数据的方式!!
python2和python3中使用range时
python2中会直接生成数据,数据大容易崩(非常占内存)
python3中只是生成了range的范围,也就是上面所说的方式
好处:节省内存!!!!
自定义生成器(函数):
包含了yield关键字的函数,就是生成器函数.
def my_generator():
yield 10
def my_generator():
for x in range(100):
yield x
生成器本质上就是迭代器:
# 借助collections包中的Iterator类,
from collections import Iterator,Iterable
print(isinstance(g,Iterator))
# 既然是迭代器,就使用for循环迭代!!
for x in g:
print(x)
for循环本质上就是调用生成器的next方法,即:
第一种触发生成器的方式:使用next方法!!!
带有赋值语句的生成器:
第一次比较特殊:只执行一,三部分
从第二次开始:一,二,三执行.
...
第二种触发生成器的方式:使用send方法.
根据定义时是否有赋值语句,以及两种触发方式:一共有四种组合!!!
1.没有赋值语句,使用next触发
def my_generator():
for x in range(5):
yield x
g = my_generator()
print(next(g))
print(next(g))
print(next(g))
print(next(g))
2.没有赋值语句,使用send触发
def my_generator():
for x in range(5):
yield x
g = my_generator()
print(g.send(None))
print(g.send(10))
print(g.send(20))
print(g.send(30))
由于没有变量接收,传递的值,实际上都丢弃了!!!
3.有赋值语句,使用next触发
# 有赋值语句,使用next触发
def my_generator(n):
for x in range(n+1):
n = yield x
print(n)
g = my_generator(5)
print(next(g))
print(next(g))
print(next(g))
由于没有使用send,使用next方法,实际上每次都是传递的None
4.有赋值语句,使用send触发
def my_generator(n):
total = 0
for x in range(n+1):
n = yield total
total += n
g = my_generator(5)
print(g.send(None))
print(g.send(1))
print(g.send(2))
print(g.send(3))
print(g.send(4))
应用:
# 计算移动平均值!
def my_generator(n):
total = 0
count = 0
avg = 0
for x in range(n+1):
n = yield avg
total += n
count += 1
avg = total / count
g = my_generator(5)
print(g.send(None))
print(g.send(10))
print(g.send(20))
print(g.send(30))
print(g.send(40))
总结:
包含yield关键字的函数定义,就是生成器函数,生成器函数也是一个迭代器!
生成器函数的使用:
通常情况下,是使用for循环的方式使用生成器.实际底层使用的是迭代器的next方法.所以,也可以使用next()方法直接触发生成器产生一个值.
针对是否有赋值语句,以及是否使用传值的方式触发生成器,一共有四种形式:
1.没有赋值语句,使用next方法触发
2.没有赋值语句,使用send方法触发.此时实际上传递的值都被丢弃掉了.
3.有赋值语句,使用next方法触发,此时实际上传递的都是None值.
4.有赋值语句,使用send方法触发,此时值才被真正的传递给了yield表达式中.
注意有赋值语句的表达式的执行顺序问题:
第一次使用send赋值,只能是None值,或者使用next方法触发,实际上也是传递了None值.
从第二次开始,可以传递有意义的值.
执行的顺序是:先给赋值语句左侧的变量赋值,然后执行yield语句下面的语句,最后返回的是yield关键字后面的值.
装饰器:decorator
对原函数进行功能上的增强!
def wrapper(fn):
# return fn
# 定义嵌套函数,
def inner(*args,**kwargs):
# 对原始函数的调用
ret = fn(*args,**kwargs)
# 对原始函数的返回值进行处理
if ret < 0:
return -ret
else :
return ret
# 把嵌套函数返回
return inner
@wrapper
def func(a,b):
return a - b
print(func(5,3))
print(func(5,30))
多层
# 定义装饰器
def w(min_value,max_value):
def wrapper(fn):
# return fn
# 定义嵌套函数,
def inner(*args,**kwargs):
# 对原始函数的调用
ret = fn(*args,**kwargs)
# 对原始函数的返回值进行处理
if ret < min_value:
return min_value
elif ret > max_value:
return max_value
# 把嵌套函数返回
return inner
return wrapper
@w(-5,5)
def fun(a,b):
return a + b
总结:
装饰器函数一定是一个闭包函数,在函数内部定义一个函数,并返回.
def wrapper(fn):
def inner(*args,**kwargs):
print('hello') # 前置增强写在这里
ret = fn(*args,**kwargs) # 原始函数调用在这里
return ret
return inner
装饰器函数的两种使用方式:
1.在被装饰的函数上使用@装饰器名
2.使用普通的函数名绑定的方式
func = wrapper(func)
不论哪种方式,目的都是一个,让装饰器函数返回的函数绑定回原始的函数名.
注意:装饰器函数中的可执行语句会被马上执行!!!
带有参数的函数装饰器(三层函数的嵌套)