您的位置:首页 > 编程语言 > Python开发

【Python学习】python学习手册--第二十章 迭代和解析,第二部分

2018-03-19 17:55 246 查看
列表解析是在一个序列的每个元素上,应用一个表达式后将其结果重新构建成为一个新的序列。对于列表解析的一把表达式来说,最外层是嵌套了方括号,方括号标识了返回值为列表,在方括号内描述了列表产生的方式。【列表解析第一部分

增加测试和嵌套循环

列表解析内可以增加多个测试,并且嵌套很多循环,一般的表达式格式可以写成如下形式:



当有for循环分句嵌套在列表解析中时,工作起来就像for复合语句的嵌套一样。

列表解析在矩阵(或多维数组)中的应用也十分广泛。

理解列表解析

如果对列表解析并不是很熟悉的开发者来说,建议使用map函数或者简单for循环嵌套,与可读性相比,编码的简洁就没那么重要了。尽管在某些情况下列表解析有性能上的优势,map调用比等效的for循环要快两倍,列表解析比map函数要稍快一点。map和列表解析都是在c语言的基础上执行的,而for循环是在PVM环境下运行。

重访迭代器:生成器

生成器函数:编写常规的def函数,但是在返回值时使用yield语句,一次返回一个结果,并在结果与结果之间保存函数状态以继续执行函数。

生成器表达式类似于上一节中所讲的列表解析,但是它们返回按需产生结果的一个对象,而不是构建一个结果列表。

二者都不会在一次运行中产生完整的列表,这样可以节省内存空间,并且将计算时间分散到每次请求结果的执行中。

生成器函数

它与传统的函数不同,传统的函数通过return语句返回或者默认返回值后,直接退出了函数的执行,然而也有可能编写一个送回值并保存运行状态以便随后从退出的地方继续执行的函数,这样的函数叫做生成器函数,因为它随着时间产生一个序列。

状态挂起

当生成器函数生成值的时刻,会挂起函数状态并继续执行函数。生成器函数在挂起时,会保存函数的作用域,当函数恢复时,能够从之前退出的状态继续执行。生成器函数是在yield语句地方挂起该函数并向调用者返回一个值,还保存了函数的执行状态,以便在后续调用时能够从当前语句继续执行。从函数的角度来讲,生成器函数是随着时间返回一个一个值,而不是像传统函数那样,一次性就返回一系列的值。

迭代协议整合

要支持迭代协议,就要有__next__方法,该方法要么返回下一项,要么抛出StopIteration异常来终止。生成器函数内包含一个yield语句,该语句特别编译为生成器,当调用它时,会返回一个可迭代对象
be2c
,该对象支持__next__方法来继续执行函数。从调用者的角度来讲,迭代器的每一次迭代(每一次执行__next__方法)都会执行到yield语句处返回一个值或引发一个StopIteration异常

>>> def f(x):
...    start=10
...    for i in range(x):
...       start=i+start
...       yield start
...
>>> test=f(10)
>>> list(test)
[10, 11, 13, 16, 20, 25, 31, 38, 46, 55] #start变量的值被记录下来,每一次迭代时,都会从yield语句处继续执行
>>> test
<generator object f at 0x7ff41d592fc0>        #生成对象实际上是一个生成器对象,也具有可迭代性
>>>


尽管相同的功能都可以用常规函数直接生成,但是生成器函数每一次调用返回一个值而不是整个序列(节省了内存),并且把产生列表的时间分散到每个迭代的时间上。所以生成器在内存使用和性能方面都更好。有了生成器,函数变量就能进行自动保存和恢复。

扩展生成器函数协议:send和next

send方法生成一系列结果的下一个元素,它也提供了一种调用者与生成器之间进行通信的方法,从而影响它的操作。当使用send协议时,通过调用G.send(value)来发送传入的值,并且将该值通过yield返回。如果提前正常调用了生成器,那么在没有传入值时,yield将会返回会None:

>>> def f():
...    for i in range(5):
...      x=yield i
...      print(x)
...
>>> f
<function f at 0x7ff41d5a0400>
>>> f()
<generator object f at 0x7ff41ce1c678>
>>> gen=f()
>>> next(gen)      #开始执行生成器函数,执行到yield语句,会返回零值
0
>>> next(gen)      #没有send进入相关值,所以yield语句返回None.即print输出None.
None
1                  #执行到yield语句返回的i值,为1
>>> gen.send(88)
88                 #传入的88值,赋值给了x变量,并打印了出来
2                  #执行到yield语句返回了值2
>>>


生成器表达式

生成器表达式与列表解析有很大类似的地方,从语法上看到生成器表达式是用圆括号()生成,而列表解析是用方括号[]。

>>> [x**2 for x in range(5)]
[0, 1, 4, 9, 16]
>>> (x**2 for x in range(5))
<generator object <genexpr> at 0x7ff41ce1c6d0>      #产生一个生成器对象
>>> list(x**2 for x in range(5))
[0, 1, 4, 9, 16]
>>>


生成器表达式大体上可以认为是对内存空间的优化,它们不需要像方括号的列表解析一样,一次构造出整个列表。

生成器是一个单迭代器对象,意味着多个迭代工具在使用这个迭代器时,它们都会指向相同的位置,不会重复出现不同的迭代序列,一旦一个迭代器运行完成,其它迭代器都会用尽。此时必须产生一个新的迭代器继续使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: