Python 新式类与 __subclasses__ 函数

17 投票
2 回答
18556 浏览
提问于 2025-04-15 22:57

有人能解释一下为什么这个在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 个回答

3

补充一下上面的回答。

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 类。

希望这能帮到你。

34
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__']

撰写回答