问题的简短版本:为什么生成器实例的类型不是创建实例的生成器函数?也就是说,如果我们有一个生成器,说def G(): yield 1
而g
是由G
创建的生成器实例,即g = G()
,那么为什么type(g) is G
为false ?
详细说明:
我最近问了一个关于如何确定给定生成器对象的生成器类型的问题。 (见In Python, is there any way to test a generator object to find out which generator created it?。)答案最终要求使用__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
吗?或者,对于我未能看到的所有生成器实例选择泛型类型有更深层次的原因吗?
答案 0 :(得分:5)
特别是,Python对类型的处理非常一致:&#34;创建者&#34;一个对象变成了它的类型。
这仅适用于课程。并非创建对象的所有内容都是类:
type(open('whatever')) is not open
type(iter(whatever)) is not iter
type(compile('whatever')) is not compile
生成器函数是一个函数,而不是一个类。你不能对它进行子类化,或者在它上面定义方法,或者让它从某些东西继承,或者改变它的元类,或者做几乎你可以用真实类做的任何事情。学习创建生成器的函数不会改变与它交互的方式,就像学习对象类改变了与对象交互的方式一样。与不同的类不同,无论创建生成器的函数是什么,它都提供完全相同的API。
简而言之,生成函数类型而不是函数没有任何好处。
答案 1 :(得分:4)
在上面,当我们有g = G()时,实际上是由对象g创建的 G.它的类型是否应该等于G?
如果type(g)
确实G
,则在以下示例type(f)
中F
而不是int
。
def F(): return 42
f = F()
或者我是否误解了你的观点?
G
是一个函数,而不是一个类,即不是<class 'type'>
类型的对象。
答案 2 :(得分:0)
简短回答是函数对象不是类型,函数与它返回的对象没有关系。
要检查生成器是否由函数创建,您可以比较代码对象:
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)