- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Python中的迭代器与生成器高级用法解析由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
迭代器 。
迭代器是依附于迭代协议的对象——基本意味它有一个next方法(method),当调用时,返回序列中的下一个项目。当无项目可返回时,引发(raise)StopIteration异常.
迭代对象允许一次循环。它保留单次迭代的状态(位置),或从另一个角度讲,每次循环序列都需要一个迭代对象。这意味我们可以同时迭代同一个序列不只一次。将迭代逻辑和序列分离使我们有更多的迭代方式.
调用一个容器(container)的__iter__方法创建迭代对象是掌握迭代器最直接的方式。iter函数为我们节约一些按键.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
>>> nums
=
[
1
,
2
,
3
]
# note that ... varies: these are different objects
>>>
iter
(nums)
<listiterator
object
at ...>
>>> nums.__iter__()
<listiterator
object
at ...>
>>> nums.__reversed__()
<listreverseiterator
object
at ...>
>>> it
=
iter
(nums)
>>>
next
(it)
# next(obj) simply calls obj.next()
1
>>> it.
next
()
2
>>>
next
(it)
3
>>>
next
(it)
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
StopIteration
|
当在循环中使用时,StopIteration被接受并停止循环。但通过显式引发(invocation),我们看到一旦迭代器元素被耗尽,存取它将引发异常.
使用for...in循环也使用__iter__方法。这允许我们透明地开始对一个序列迭代。但是如果我们已经有一个迭代器,我们想在for循环中能同样地使用它们。为了实现这点,迭代器除了next还有一个方法__iter__来返回迭代器自身(self).
Python中对迭代器的支持无处不在:标准库中的所有序列和无序容器都支持。这个概念也被拓展到其它东西:例如file对象支持行的迭代.
1
2
3
|
>>> f
=
open
(
'/etc/fstab'
)
>>> f
is
f.__iter__()
True
|
file自身就是迭代器,它的__iter__方法并不创建一个单独的对象:仅仅单线程的顺序读取被允许.
生成表达式 第二种创建迭代对象的方式是通过 生成表达式(generator expression) ,列表推导(list comprehension)的基础。为了增加清晰度,生成表达式总是封装在括号或表达式中。如果使用圆括号,则创建了一个生成迭代器(generator iterator)。如果是方括号,这一过程被‘短路'我们获得一个列表list.
1
2
3
4
5
6
|
>>> (i
for
i
in
nums)
<generator
object
<genexpr> at
0x
...>
>>> [i
for
i
in
nums]
[
1
,
2
,
3
]
>>>
list
(i
for
i
in
nums)
[
1
,
2
,
3
]
|
在Python 2.7和 3.x中列表表达式语法被扩展到 字典和集合表达式。一个集合set当生成表达式是被大括号封装时被创建。一个字典dict在表达式包含key:value形式的键值对时被创建:
1
2
3
4
|
>>> {i
for
i
in
range
(
3
)}
set
([
0
,
1
,
2
])
>>> {i:i
*
*
2
for
i
in
range
(
3
)}
{
0
:
0
,
1
:
1
,
2
:
4
}
|
如果您不幸身陷古老的Python版本中,这个语法有点糟:
1
2
3
4
|
>>>
set
(i
for
i
in
'abc'
)
set
([
'a'
,
'c'
,
'b'
])
>>>
dict
((i,
ord
(i))
for
i
in
'abc'
)
{
'a'
:
97
,
'c'
:
99
,
'b'
:
98
}
|
生成表达式相当简单,不用多说。只有一个陷阱值得提及:在版本小于3的Python中索引变量(i)会泄漏.
生成器 。
生成器是产生一列结果而不是单一值的函数.
第三种创建迭代对象的方式是调用生成器函数。一个 生成器(generator) 是包含关键字yield的函数。值得注意,仅仅是这个关键字的出现完全改变了函数的本质:yield语句不必引发(invoke),甚至不必可接触。但让函数变成了生成器。当一个函数被调用时,其中的指令被执行。而当一个生成器被调用时,执行在其中第一条指令之前停止。生成器的调用创建依附于迭代协议的生成器对象。就像常规函数一样,允许并发和递归调用。 当next被调用时,函数执行到第一个yield。每次遇到yield语句获得一个作为next返回的值,在yield语句执行后,函数的执行又被停止.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>>
def
f():
...
yield
1
...
yield
2
>>> f()
<generator
object
f at
0x
...>
>>> gen
=
f()
>>> gen.
next
()
1
>>> gen.
next
()
2
>>> gen.
next
()
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
StopIteration
|
让我们遍历单个生成器函数调用的整个历程.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
>>>
def
f():
...
print
(
"-- start --"
)
...
yield
3
...
print
(
"-- middle --"
)
...
yield
4
...
print
(
"-- finished --"
)
>>> gen
=
f()
>>>
next
(gen)
-
-
start
-
-
3
>>>
next
(gen)
-
-
middle
-
-
4
>>>
next
(gen)
-
-
finished
-
-
Traceback (most recent call last):
...
StopIteration
|
相比常规函数中执行f()立即让print执行,gen不执行任何函数体中语句就被赋值。只有当gen.next()被next调用,直到第一个yield部分的语句才被执行。第二个语句打印-- middle --并在遇到第二个yield时停止执行。第三个next打印-- finished --并且到函数末尾,因为没有yield,引发了异常.
当函数yield之后控制返回给调用者后发生了什么?每个生成器的状态被存储在生成器对象中。从这点看生成器函数,好像它是运行在单独的线程,但这仅仅是假象:执行是严格单线程的,但解释器保留和存储在下一个值请求之间的状态.
为何生成器有用?正如关于迭代器这部分强调的,生成器函数只是创建迭代对象的又一种方式。一切能被yield语句完成的东西也能被next方法完成。然而,使用函数让解释器魔力般地创建迭代器有优势。一个函数可以比需要next和__iter__方法的类定义短很多。更重要的是,相比不得不对迭代对象在连续next调用之间传递的实例(instance)属性来说,生成器的作者能更简单的理解局限在局部变量中的语句.
还有问题是为何迭代器有用?当一个迭代器用来驱动循环,循环变得简单。迭代器代码初始化状态,决定是否循环结束,并且找到下一个被提取到不同地方的值。这凸显了循环体——最值得关注的部分。除此之外,可以在其它地方重用迭代器代码.
双向通信 每个yield语句将一个值传递给调用者。这就是为何PEP 255引入生成器(在Python2.2中实现)。但是相反方向的通信也很有用。一个明显的方式是一些外部(extern)语句,或者全局变量或共享可变对象。通过将先前无聊的yield语句变成表达式,直接通信因PEP 342成为现实(在2.5中实现)。当生成器在yield语句之后恢复执行时,调用者可以对生成器对象调用一个方法,或者传递一个值 给 生成器,然后通过yield语句返回,或者通过一个不同的方法向生成器注入异常.
第一个新方法是send(value),类似于next(),但是将value传递进作为yield表达式值的生成器中。事实上,g.next()和g.send(None)是等效的.
第二个新方法是throw(type, value=None, traceback=None),等效于在yield语句处 。
1
|
raise
type
, value, traceback
|
不像raise(从执行点立即引发异常),throw()首先恢复生成器,然后仅仅引发异常。选用单次throw就是因为它意味着把异常放到其它位置,并且在其它语言中与异常有关.
当生成器中的异常被引发时发生什么?它可以或者显式引发,当执行某些语句时可以通过throw()方法注入到yield语句中。任一情况中,异常都以标准方式传播:它可以被except和finally捕获,或者造成生成器的中止并传递给调用者.
因完整性缘故,值得提及生成器迭代器也有close()方法,该方法被用来让本可以提供更多值的生成器立即中止。它用生成器的__del__方法销毁保留生成器状态的对象.
让我们定义一个只打印出通过send和throw方法所传递东西的生成器.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
>>>
import
itertools
>>>
def
g():
...
print
'--start--'
...
for
i
in
itertools.count():
...
print
'--yielding %i--'
%
i
...
try
:
... ans
=
yield
i
...
except
GeneratorExit:
...
print
'--closing--'
...
raise
...
except
Exception as e:
...
print
'--yield raised %r--'
%
e
...
else
:
...
print
'--yield returned %s--'
%
ans
>>> it
=
g()
>>>
next
(it)
-
-
start
-
-
-
-
yielding
0
-
-
0
>>> it.send(
11
)
-
-
yield
returned
11
-
-
-
-
yielding
1
-
-
1
>>> it.throw(IndexError)
-
-
yield
raised IndexError()
-
-
-
-
yielding
2
-
-
2
>>> it.close()
-
-
closing
-
-
|
注意: next还是__next__?
在Python 2.x中,接受下一个值的迭代器方法是next,它通过全局函数next显式调用,意即它应该调用__next__。就像全局函数iter调用__iter__。这种不一致在Python 3.x中被修复,it.next变成了it.__next__。对于其它生成器方法——send和throw情况更加复杂,因为它们不被解释器隐式调用。然而,有建议语法扩展让continue带一个将被传递给循环迭代器中send的参数。如果这个扩展被接受,可能gen.send会变成gen.__send__。最后一个生成器方法close显然被不正确的命名了,因为它已经被隐式调用.
链式生成器 注意: 这是PEP 380的预览(还未被实现,但已经被Python3.3接受) 。
比如说我们正写一个生成器,我们想要yield一个第二个生成器——一个子生成器(subgenerator)——生成的数。如果仅考虑产生(yield)的值,通过循环可以不费力的完成:
1
2
3
|
subgen
=
some_other_generator()
for
v
in
subgen:
yield
v
|
然而,如果子生成器需要调用send()、throw()和close()和调用者适当交互的情况下,事情就复杂了。yield语句不得不通过类似于前一章节部分定义的try...except...finally结构来保证“调试”生成器函数。这种代码在PEP 380中提供,现在足够拿出将在Python 3.3中引入的新语法了:
1
|
yield
from
some_other_generator()
|
像上面的显式循环调用一样,重复从some_other_generator中产生值直到没有值可以产生,但是仍然向子生成器转发send、throw和close.
最后此篇关于Python中的迭代器与生成器高级用法解析的文章就讲到这里了,如果你想了解更多关于Python中的迭代器与生成器高级用法解析的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我使用以下代码和嵌套生成器迭代文本文档并使用 get_train_minibatch() 返回训练示例。我想保留( pickle )生成器,这样我就可以回到文本文档中的相同位置。但是,您不能 pick
在本教程中,您将借助示例了解 JavaScript 生成器。在 JavaScript 中,生成器提供了一种使用函数和迭代器的新方法。 使用生成器, 您可以从函数内部的任何位置停止执行函数 并从
LESS is very cool .我一直想知道是否有任何好的 html 生成器可以让我更轻松地编写表单或做其他事情。除了 html,是否有一些类似的东西? 最佳答案 已尝试 Haml ? 从它的网
前言 如果是做python或者其他语言的小伙伴,对于生成器应该不陌生。但很多php开发者或许都不知道生成器这个功能,可能是因为生成器是php 5.5.0才引入的功能,也可以是生成器作用不是很明显。
我正在尝试编写一个使用生成器语法生成日期时间列表的函数: let dateRange = let endDate = System.DateTime.Parse("6/1/2010")
我遇到了一些看起来像的代码: [func(val) for val in iterable] 有一个可迭代对象(在我的例子中是一个生成器),用户想要为其副作用调用每个值的函数(例如 func 可以只是
Delphi 有内置的东西来生成 UUID 吗? 最佳答案 program Guid; {$APPTYPE CONSOLE} uses SysUtils; var Uid: TGuid; Result
我正在深入研究 javascript 生成器,但我真的很困惑。 我使用 node@0.11.x 运行此示例: function find() { process.nextTick(functi
有人知道一些关于如何为 hibernate 创建自定义 ID 生成器的好教程吗? 最佳答案 在 Google 上粗略搜索“hibernate 自定义 id 生成器教程”发现了以下可能性。我排除了那些看
我正在关注 Python 大师 David Beazley 的幻灯片。它指出“生成器也用于并发。这是一个示例: from collections import deque def countdown(
我有一个生成事件的生成器,我想用可以从 API 获取的附加元数据来丰富它。 某些事件具有与其链接的对象 ID,而其他事件则具有对象的哈希值,但不能同时具有两者。我无法根据哈希获取对象 id,我只能执行
假设我有一个自定义类: public class CustomClass { private String name; private String data; public
我正在考虑实现一个函数来在 SQL 请求中“构建”WHERE 子句,如下所示: "SELECT * FROM table $where" 使用如下所示的循环构建 $where: $arr=array(
我正在寻找执行此操作的标准函数: def Forever(v): while True: yield v 这看起来太琐碎了,我不敢相信没有标准版本。 就此而言,有人知道指向所有标准生成器函
我知道这个网站上有几个非常相似的相关问题,但是在看了这部剧之后,我相信这个问题本身就是独一无二的。如果有人能找到并提供证据证明我的问题完全被骗了,我会自己撤回它(所以请不要否决这个!)。 我是 Jav
void __fastcall TForm1::Button1Click(TObject *Sender) { int size = MemoEnter->GetTextLen() + 1;
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我试图在我的生成器的以下两个定义之间做出决定。哪个更好?哪个“更像 python ”?无论如何,有没有办法减轻每一个的缺点? def myGenerator1(howMany): result
我有一个 Python 生成器 lexg,它在每次迭代时生成一个列表。该代码似乎在传统的 for 循环意义上工作,即 for i in lexg(2,2): print(i) 产生: [2, 0] [
我希望这不会超出 Python 生成器的能力,但我想构建一个这样,每次调用该函数时,它都会返回下一分钟直到结束时间。 因此该函数读取开始时间和结束时间,并以分钟为单位返回时间,直到涵盖其间的所有时间。
我是一名优秀的程序员,十分优秀!