[英]Why are Python's generators not types?
問題的簡短版本:為什么生成器實例的類型不是創建實例的生成器函數? 也就是說,如果我們有一個生成器,比如def G(): yield 1
和g
是由G
創建的生成器實例,即g = G()
,那么為什么type(g) is G
false?
細節:
我最近問了一個關於如何確定給定生成器對象的生成器類型的問題。 (參見Python,有沒有辦法測試生成器對象以找出創建它的生成器? )答案最終要求使用__name__
。 但是,這仍然給我一個未解答的問題:為什么生成器不是Python中的類型?
下面的所有代碼都考慮到了Python 3.3,但我相信大多數代碼都適用於舊版本的Python(至少2.7版本)和更新版本,即3.4版本。
讓我們看一個非常簡單的迭代器示例,該迭代器首先作為類實現:
class C(object):
def __iter__(self):
return self
def __next__(self):
return 1
c = C()
print(next(c)) # 1
print(type(c)) # <class '__main__.C'>
print(type(c) is C) # True
在功能上,上面的類與下面給出的生成器相同。 (當然,我們缺少throw
, send
和close
,還有其他微妙的差異,但我認為它們與手頭的問題無關。)
def G():
while True:
yield 1
g = G()
print(next(g)) # 1
print(type(g)) # <class 'generator'>
print(type(g) is G) # False
但正如您所看到的,使用def
和yield
定義的生成器創建的對象類型以及使用類創建的迭代器的處理方式完全不同。 所有生成器實例都被賦予泛型類型generator
。 對我來說,這類似於被賦予泛型類型的類創建的所有對象,比如object
,當然不是這種情況。
Python總體上是一種非常一致和邏輯的語言。 特別是,Python對類型的處理非常一致:對象的“創建者”成為它的類型。 即使使用元類,這也很好用:
class M(type):
def __new__(cls, *args):
return super().__new__(cls, *args)
class C(metaclass=M):
pass
c = C()
print(type(c) is C) # True
print(type(C) is M) # True
print(type(M) is type) # True
print(type(type) is type) # True
你可以在這里看到type(c)
是C
因為c
是由C
創建的,而type(C)
是M
因為類對象C
是由元類M
創建的,最后M
本身是一個由對象創建的類對象。元type
。 我們甚至有type(type)
是type
,作為自引用,它為類型層次結構提供根。
但是,對於生成器,我覺得這種一致性被打破了。 在上面,當我們有g = G()
,對象g
實際上是由G
創建的。 不應該將其類型設置為等於G
嗎? 或者,對於我未能看到的所有生成器實例選擇泛型類型有更深層次的原因嗎?
特別是,Python對類型的處理非常一致:對象的“創建者”成為它的類型。
這僅適用於課程。 並非創建對象的所有內容都是類:
type(open('whatever')) is not open
type(iter(whatever)) is not iter
type(compile('whatever')) is not compile
生成器函數是函數,而不是類。 你不能對它進行子類化,或者在它上面定義方法,或者讓它從某些東西繼承,或者改變它的元類,或者做幾乎你可以用真實類做的任何事情。 學習創建生成器的函數不會改變與它交互的方式,就像學習對象類改變了與對象交互的方式一樣。 與不同的類不同,無論創建生成器的函數是什么,它都提供完全相同的API。
簡而言之,生成函數類型而不是函數沒有任何好處。
在上面,當我們有g = G()時,對象g實際上是由G創建的。它的類型是否應該設置為等於G?
如果type(g)
確實是G
,那么在下面的示例中, type(f)
將是F
而不是int
。
def F(): return 42
f = F()
或者我誤解了你的觀點?
G
是一個函數,而不是一個類,即不是<class 'type'>
類型的對象。
簡短的回答是函數對象不是類型,函數與它返回的對象沒有關系。
要檢查生成器是否由函數創建,您可以比較代碼對象:
def f():
yield 1
def g():
yield 1
t = f()
def is_generator_created_by(g, func):
return g.gi_code is func.func_code
print is_generator_created_by(t, f), is_generator_created_by(t, g)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.