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'
posted @ 2020-03-24 16:25  the3times  阅读(577)  评论(0编辑  收藏  举报