[英]How to inspect Generator type object?
使用以下代码(第一种情况),
def f():
mylist = range(3)
for i in mylist:
yield i*i
如果不检查y
,您可以说y=f()
返回collections.abc.Generator
类型的对象(x*x for x in range(3))
吗?
使用以下代码(第二种情况),
def func():
x = 1
while 1:
y = yield x
x += y
调用y=func()
时返回的Generator
类型对象是什么? 你如何检查y
看到代码?
生成器表达式 (x*x for x in range(3))
与您描述的简单生成器函数大致相同。 但是,对genexp进行范围界定可能会稍微复杂一些(这就是为什么我们通常建议您立即使用生成器表达式,而不是传递它们)。
y = yield x
的代码是增强型生成器的示例,该生成器用于将数据发送到运行中的生成器,本质上在运行中的生成器和调用代码之间创建了双向通信通道。
发送/接收逻辑的主要用例是实现协程和生成器蹦床。 请参阅David Beazley的蹦床示例 。
增强的生成器是Twisted Python美丽的内联回调 (实现协程)的关键 。
对于y = func()
的变量y ,唯一的检查技术是检查公共API:
>>> y = func()
>>> dir(y)
['__class__', '__delattr__', '__doc__', '__format__',
'__getattribute__', '__hash__', '__init__', '__iter__',
'__name__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'close', 'gi_code', 'gi_frame',
'gi_running', 'next', 'send', 'throw']
对于生成器函数本身,可以使用dis模块检查代码以查看其工作方式:
>>> def func():
x = 1
while 1:
y = yield x
x += y
>>> import dis
>>> dis.dis(func)
3 0 LOAD_CONST 1 (1)
3 STORE_FAST 0 (x)
4 6 SETUP_LOOP 21 (to 30)
5 >> 9 LOAD_FAST 0 (x)
12 YIELD_VALUE
13 STORE_FAST 1 (y)
6 16 LOAD_FAST 0 (x)
19 LOAD_FAST 1 (y)
22 INPLACE_ADD
23 STORE_FAST 0 (x)
26 JUMP_ABSOLUTE 9
29 POP_BLOCK
>> 30 LOAD_CONST 0 (None)
33 RETURN_VALUE
您可以使用pdb调试器逐步跟踪代码。
>>> import pdb
>>> y = func()
>>> pdb.runcall(next, y)
> /Users/raymond/Documents/tmp.py(2)func()
-> x = 1
(Pdb) s
> /Users/raymond/Documents/tmp.py(3)func()
-> while 1:
(Pdb) s
> /Users/raymond/Documents/tmp.py(4)func()
-> y = yield x
(Pdb) p locals()
{'x': 1}
(Pdb) s
> /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/bdb.py(440)runcall()
-> self.quitting = 1
(Pdb) s
1
>>> pdb.runcall(y.send, 10)
> /Users/raymond/Documents/tmp.py(5)func()->1
-> x += y
(Pdb) s
> /Users/raymond/Documents/tmp.py(4)func()->1
-> y = yield x
(Pdb) locals()
{'__return__': 1, 'x': 11, 'y': 10}
生成器的目标是按需生成值。 换句话说,如果您需要整数的平方,但是您事先不知道有多少整数,则可以使用generators
来生成值。 这是通过使用yield
而不是return
来实现的。 After yield
函数确实保留上下文。 您可以将yield
想象为“暂停”,而将收益想象为“ stop”。 生成器的另一个好处是,即使您知道集合的大小,也不会一次将所有内容加载到内存中,而一次只消耗一个元素。
在第二个示例中,您将收到以下错误:
for i in func():
print(i)
因为y = yield x
表达式需要send()方法调用:请参见https://www.python.org/dev/peps/pep-0342/#specification-sending-values-into-generators
“ for”语句调用iter(foo())
函数,该函数返回生成器对象。 然后,“ for”语句将调用next(generator),而generator不会引发StopIteration
异常。
对于第二个示例:
for i in func():
print(i)
“ for”语句将获取生成器对象,然后为其调用next(generator object)
。 对于此调用,它将获得x值(1)。 此时,生成器对象正在等待send(something)
方法调用,该调用将将something值设置为y。 如果不调用此方法,则next(generator object)
调用将向None
发送None
。 在这种情况下,y将取None
值,并且程序将在x += y
上引发Error
您的第二个示例的正确用法是:
f = func()
# open generator object
next(f)
#1
f.send(2)
#3
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.