[英]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.