簡體   English   中英

如何檢查Generator類型的對象?

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM