今天我们来讲述Python核心笔记2- yield 函数创建生成器方式
1、生成器
在 Python 中,使用了 yield 的函数被称为生成器(generator),一边循环一边计算的机制;
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
好处:节省内存空间;适用想要得到庞大的数据,又想让它占用空间少的场景;
1.1 创建生成器方式1
第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )
# 原有方式
test_list = [x*2 for x in range(5)] # 执行后test_list : [0, 2, 4, 6, 8]
# 生成器
test_gen = (x*2 for x in range(5)) # 执行后test_gen : <generator object <genexpr> at 0x106d2c820>
# 生成器的打印
next(test_gen) # 执行后打印 0
next(test_gen) # 执行后打印 2
next(test_gen) # 执行后打印 3
# 遍历test_gen
for x in test_gen:
print(x) # 打印 0 2 4 6 8
注意:
通过next()方式超出边界会报StopIteration 异常;
创建了一个生成器后,基本上永远不会调用 next() ,而是通过 for 循环来迭代它,并且不需要关心 StopIteration 异常。
1.2 创建生成器方式2
如果一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。调用函数就是创建了一个生成器(generator)对象。
# 斐波那契数列, 原有写法
def fib(times):
n = 0
a,b = 0,1
while n < times:
print(b)
a,b = b, a+b
n += 1
return True
fib(5) # 打印结果:1, 1, 2, 3, 5, 8
# 斐波那契, 生成器方式
def fib(times):
n = 0
a,b = 0,1
while n < times:
yield b
a,b = b, a+b
n += 1
return True
obj_fit = fib(5)
# 逐次d打印
next(obj_fit) # 打印结果:1
next(obj_fit) # 打印结果:1
next(obj_fit) # 打印结果:2
# 打印返回的迭代器
for x in obj_fit:
print x # 打印结果 1, 1, 2, 3, 5, 8
注:
yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的下一条语句开始执行。
1.3 send方法
.send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果。
def test():
i = 0
while i < 5:
temp = yield i
print(temp)
print(i)
i += 1
t=test()
next(t)
t.send('test')
next(t)
# 代码执行结果:hshs 0 None 1
2、迭代器
迭代是访问集合元素的一种方式;
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束;
迭代器只能往前不会后退。
2.1 可迭代对象
以直接作用于 for 循环的数据类型有以下几种:
一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;
一类是 generator ,包括生成器和带 yield 的generator function。
这些可以直接作用于 for 循环的对象统称为可迭代对象:Iterable 。
2.2 是否可迭代
可以使用 isinstance() 判断一个对象是否是 Iterable 对象;
from collections import Iterable
print (isinstance([], Iterable)) # 打印 True
2.3 迭代器
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用 isinstance() 判断一个对象是否是 Iterator 对象
print(isinstance((x for x in range(10)), Iterator)) # 打印ture
print(isinstance([], Iterator)) # 打印false
生成器都是 Iterator 对象,但 list 、 dict 、 str 虽然是 Iterable ,却不是 Iterator 。
把 list 、 dict 、 str 等 Iterable 变成 Iterator 可以使用 iter() 函数;
print(isinstance([], Iterator)) # 打印true
3、闭包
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包.
def test_bb(numa):
def test_inner(numb):
return numa + numb
return test_inner
tb = test_bb(10) # tb指向test_inner
result = tb(5) # 调用test_inner
print(result) # 打印 15
3.1 实例
闭包可以提高代码复用;
例如下面案例,如果没有闭包我们需要每次创建直线函数的时候同时说明a,b,x,需要多参数传递;
def line_conf(a, b):
def line(x):
return a*x + b
return line
line1 = line_conf(100,100)
line2 = line_conf(100,100)
line1(10)
line2(20)
4、装饰器
装饰器是闭包的一种应用,用于拓展原来函数功能的一种方式,好处就是在不用更改原函数的代码前提下给函数增加新的功能。
4.1 简单装饰器
在执行执行方法的上面增加一个帽子(装饰这个方法),格式为 @check
def check(func):
def wrapper():
print('---check---')
func()
return wrapper
@check
def login():
print('login')
login() # 打印 ---check--- login
4.2 多个装饰器
天气太冷,一个帽子不够,那就扩展两个;
@check_one
@check_two
def check_one(func):
def wrapper():
print('---check_one---')
func()
return wrapper
def check_two(func):
def wrapper():
print('---check_two---')
func()
return wrapper
@check_one
@check_two
def login():
print('login')
login() # 打印 ---check_one--- ---check_two--- login
注:装饰器装饰的时候从下往上, 调用的时候从上往下调用;
4.3 带参数装饰器
装饰器中可以传入参数,先形成一个完整的装饰器,然后再来装饰函数;
函数如果需要传入参数也是可以的,用不定长参数符号就可以接收,例子中传入了2个参数(name, password)。
def check(user):
def outwrapper(func):
def wrapper(*args, **kwargs):
if user == 'admin':
print('---check admin---')
else:
print('---check other---')
func(*args, **kwargs)
return wrapper
return outwrapper
@check(user='admin')
def login(name, password):
print('Name:%s Pass:%s' % (name, password))
login('HAHA', 'NMNM') # 打印 ---check admin--- Name:HAHA Pass:NMNM
4.4 类装饰器
装饰器也不一定只能用函数来写,也可以使用类装饰器,实质是使用了类方法中的call魔法方法来实现类的直接调用。
class check(object):
def __init__(self, user):
self.user = user
def __call__(self, func):
def wrapper(*args, **kwargs):
if self.user == 'admin':
print('---check admin---')
else:
print('---check other---')
func(*args, **kwargs)
return wrapper
@check(user='admin')
def login(name, password):
print('login name:%s pass:%s' % (name, password))
login('HAHA', 'NMNM') # 打印 ---check admin--- Name:HAHA Pass:NMNM