Python 新式类与 __subclasses__ 函数
有人能解释一下为什么这个在Python 2.5中可以用:
class Foo(object):
pass
class Bar(Foo):
pass
print(Foo.__subclasses__())
但这个却不行:
class Foo():
pass
class Bar(Foo):
pass
print(Foo.__subclasses__())
后者会返回“AttributeError: class Foo has no attribute '__subclasses__
'”,但我不太明白为什么。我知道这和旧式类和新式类有关,但我不清楚为什么这会导致这个功能不可用。
补充说明:我想理解为什么__subclasses__()
在旧式类中不可用,我明白这个方法在旧式类中不存在,但我不明白新式类有什么特别之处,让这些新功能变得可能。
2 个回答
补充一下上面的回答。
Python 3.0 有一种独立的类实现方式。在 Python 3.0 中,子类化是一个特性,而在 Python 2.0 中没有这个特性。
所以如果你安装了 Python 3.x 并且运行:
class Foo():
pass
class Bar(Foo):
pass
print(Foo.__subclasses__())
就不会出现任何属性错误。
现在,这种功能也扩展到了 Python 2.0 的类,通过在每个类定义中继承 "object" 基类。
所以如果你在 Python 2.x 中这样做:
class Foo(object):
pass
class Bar(Foo):
pass
print(Foo.__subclasses__())
因为你在新类 Foo 中继承了 object 基类,所以就继承了 3.x 风格的类,也不会出现属性错误。
但是当你在 Python 2.x 中这样做:
class Foo():
pass
class Bar(Foo):
pass
print(Foo.__subclasses__())
这意味着没有继承新风格类,而 subclass 是新风格类的一部分,所以会抛出属性错误。
所以记住,如果你想把任何 Python 3.x 类的功能扩展到 Python 2.x,你需要在你的类定义中继承 object 类。
希望这能帮到你。
class Foo(object):
pass
上面的类是一个“新式”类,因为它继承自object类。新式类提供了很多旧式类没有的额外功能。新式类的一个特别之处是可以通过__subclasses__方法来确定这个类的子类。
关于新式类和__subclasses__方法,有一些不错的讨论,这些讨论曾经完全没有文档说明过。虽然这里有Tim Peters的非官方解释。
“每个新式类都会保持一个对其直接子类的弱引用列表。这个方法会返回所有仍然存在的引用的列表。”
所以,回答你的问题,__subclasses__这个功能不可用是因为在你的第二个例子中:
class Foo():
pass
旧式类Foo没有继承自object(所以它不是新式类),因此也就没有继承__subclasses__方法。
注意,如果你不明白为什么旧式类没有__subclasses__方法,你可以随时打开一个Python解释器,使用dir来检查一下。
>>> class Foo(object):
... pass
...
>>> dir(Foo.__class__)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__doc__', '__
eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__instancecheck__', '__itemsize__', '__le__', '__lt
__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__s
ubclasscheck__', '__subclasses__', '__subclasshook__', '__weakrefoffset__', 'mro']
>>> class Bar():
... pass
...
>>> dir(Bar.__class__)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: class Bar has no attribute '__class__'
>>> dir(Bar)
['__doc__', '__module__']
>>> dir(Foo)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '
__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']