Python生成器

1、生成器初识

      Python 使用生成器对「延迟操作」提供了支持,所谓「延迟操作」就是在需要它的时候才产生结果,而不是说立即产生结果。

      在 Python 中,定义生成器必须要使用 yield 这个关键词,yield 翻译成中文有「生产」这方面的意思。在 Python 中,它作为一个关键词,是生成器的标志。接下来我们来看一个例子:

>>> def f():
...    yield 0
...    yield 1
...    yield 2
...
>>> f
<function f at 0x00000000004EC1E0>

      上面是写了一个很简单的 f 函数,代码块是 3 个 yield 发起的语句,下面让我们来看看如何使用它:

>>> fa = f()
>>> fa
<generator object f at 0x0000000001DF1660>
>>> type(fa)
<class 'generator'>

      上述操作可以看出,我们调用函数得到了一个生成器(generator)对象。

>>> dir(fa)
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__',
'__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']

      在上面我们看到了 __iter__() 和 __next__(),虽然我们在函数体内没有显示的写 __iter__() 和 __next__(),仅仅是写了 yield,但它就已经是「迭代器」了。既然如此,那我们就可以进行如下操作:

>>> fa = f()
>>> fa.__next__()
0
>>> fa.__next__()
1
>>> fa.__next__()
2
>>> fa.__next__()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration

      从上面的简单操作可以看出:含有 yield 关键词的函数 f() 是一个生成器对象,这个生成器对象也是迭代器。所以就有了这样的定义:把含有 yield 语句的函数称为生成器,生成器是一种用普通函数语法定义的迭代器。

      通过上面的例子可以看出,这个生成器(即迭代器)在定义的过程中并没有昨天讲的迭代器那样写 __iter__(),而是只用了 yield 语句,之后一个普普通通的函数就神奇的成了生成器,同样也具备了迭代器的特性。

      yield 语句的作用,就是在调用的时候返回相应的值。

      参考:Python生成器初识

 

2、生成器延伸。

      Python 其实有两种不同的方法来提供生成器,一种是函数形式,另一种是表达式形式,说全一点儿就是「生成器函数」和「生成器表达式」。

  • 生成器函数。

      「生成器函数」和普通的函数定义类似。区别在于普通函数使用 return 返回结果,生成器函数是用 yield 返回结果。

      yield 的作用是在调用的时候返回相应的值,一次返回一个结果,在每个结果中间挂起函数的状态(即暂停执行),下一次执行是从上次暂停的位置开始,继续向下执行。

      下面写出「将一个全是整数的列表进行操作后只保留奇数」的程序。

def get_odd(lst):
   for i in lst:
       if i % 2:
           yield i

def main():
   lst = range(10)
   for i in get_odd(lst):
       print(i)

if __name__ == '__main__':
   main()
  • 生成器表达式。

      「生成器表达式」和列表推导式类似。区别在于使用列表推导,一次会产生所有的结果,而用「生成器表达式」则不会这样,它是按需产生。

      列表推导式的写法如下:

>>> res = [x for x in range(5)]
>>> res
[0, 1, 2, 3, 4]

      生成器表达式就是将上面的 [] 变成 () 即可:

>>> res = (x for x in range(5))
>>> res
<generator object <genexpr> at 0x109d9f570>
>>> next(res)
0
>>> next(res)
1
>>> next(res)
2
>>> next(res)
3

      「生成器」的优势在「生成器表达式」中体现在,如果我们想对一系列整数求和,直接用生成器可以写成下面这样:

>>> sum(x for x in range(5))
10

      但是如果你用常规的写法去写,就会写成下面这样:

>>> sum([x for x in range(5)])
10

      上面的代码先构造了一个列表,然后再用 sum 函数求和,多了一步。光在时间效率上,就已经差别巨大。

      参考,生成器延伸

3、总结

      综上所讲,「生成器」光在明面上的优点就有好几个:代码行数更少;代码更易读;时效更高。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值