python生成器
python的迭代器很好用,如何自定义一个迭代器呢。有一种方式就是通过生成器的方式来实现迭代器机制。
python中生成器有两种:函数生成器和表达式生成器;我们自定义迭代器需要用到的是生成器函数。
生成器函数很简单,就是将普通函数的return语句
换成yield语句
,修改后这个函数就变成了一个生成器函数。
生成器
生成器generator也称生成器迭代器generator iterator
若函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即生成器对象。
生成器对象内置有__iter__
和__next__
方法,所以生成器本身就是一个迭代器。这种通过生成器函数产生的生成器对象官方称generator iterator,这是为了避免名称的混乱。因为通常提到生成器函数指的就是生成器迭代器。
def my_range(start, end, step=1):
while start < end:
yield start
start += step
g = my_range(1, 5) # <generator object my_range at 0x00000125999DB9E0>
print(g.__iter__()) # <generator object my_range at 0x00000125999DB9E0>
print(g.__next__()) # 1
print(next(g)) # 2
print(next(g)) # 3
print(next(g)) # 4
print(next(g)) # 抛出异常 StopIteration
既然生成器对象属于迭代器,那么必然可以使用for循环迭代,如下:
for i in g:
print(i)
# 有了yield关键字,我们就有了一种自定义迭代器的实现方式。
# yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值
函数生成器与普通函数不同:
- 普通函数被调用时,会将函数体内代码从上往下依次执行,遇到return语句,立即退出函数并返回返回值;再次调用函数时,函数会从头再执行一遍。
- 函数生成器被调用时,会执行函数体代码,遇到yield语句后,在yield位置出挂起并返回yield后的数据给调用者。当再次被调用时,从上次挂起位置再往下执行函数体代码。
总结:
- 普通函数:喊它进去干活,活不干完不出来;再喊他进去干活又从头开始干;
- 生成器函数:喊他进去干活,叫他出来它就出来,再喊他进去干活,它从上次干活的位置接着往下干。
yield表达式应用
生成器函数的使用yiled语句实现的,python的yield表达式也很强大。yield不仅可以从函数体内往外取值,还可以从外部往函数体内传值,传值使用generator.send(value)
def eater():
print('Ready to eat')
while True:
food = yield
print('get the food: %s, and start to eat' %food)
g = eater()
next(g) # 传值前必须先调用一次生成器,让生成器先挂起来,等待接收yield赋值给food
g.send('包子') # 通过send方法将数据传给yield赋值给food,生成器内部有循环又会再次被挂起
g.send('牛奶') # send()必须要有一个实参
g.send(None) # 如果send的是None,则默认执行next(g)
yield的这种为函数体传值的方式必须要先next(g)初始化一次,我们可以编写装饰器来完成为所有表达式形式yield对应生成器的初始化操作,如下
def init(func):
def wrapper(*args,**kwargs):
g = func(*args,**kwargs)
next(g)
return g
return wrapper
@init
def eater():
print('Ready to eat')
while True:
food = yield
print('get the food: %s, and start to eat' %food)
g = eater()
# next(g) # 有了这个装饰器,next(g)就不用管了;这个装饰器的功能也仅仅如此,没有其他作用。
g.send('牛奶')
g.send(None)
yield表达式可以同时往外返回值和往函数内传值:food = yield food_list
补充:g.close()
关闭生成器,关闭后不能再给函数体传值。
def eater():
... print('Ready to eat')
... food_list=[]
... while True:
... food=yield food_list
... food_list.append(food)
...
>>> e=eater()
>>> next(e)
Ready to eat
[]
>>> e.send('蒸羊羔')
['蒸羊羔']
>>> e.send('蒸熊掌')
['蒸羊羔', '蒸熊掌']
>>> e.send('蒸鹿尾儿')
['蒸羊羔', '蒸熊掌', '蒸鹿尾儿']
生成器表达式
生成器表达式(generator expression),即通过表达式产生的生成器。这是创建生成器的第二种方式。
>>> s = (i for i in range(10))
>>> s
<generator object <genexpr> at 0x000002094E920270>
>>> sum(s)
45
列表推导式
列表推导式:list comprehensions
my_list = [i for i in range(10) if i %2 == 0]
# mylist = [0, 2, 4, 6, 8]
此外,还有字典推导式、集合推导式、元组推导式。需要注意的是元组推导式不能使用(i for i in range(10))
,因为这个小括号被生成器表达式占用了,需要使用元组推导式的是有需要使用tuple(x for x in nums)
基本数据类型的常用推导式或者说是生成式请参考这篇博客。
三元表达式
三元表达式是简化的if-else语句
sex = 'female'
name = 'jack' if sex == 'male' else 'jane' # name = 'jane'