Python类成员查找的底层机制

3 投票
2 回答
730 浏览
提问于 2025-04-17 04:11

在Python中,如果我定义了三个类:

class A:
    name = 'oliver'
    hailstone_ending = [4,2,1]

class B:
    def __init__(self):
        self.name = 'oliver'
        self.hailstone_ending = [4,2,1]

class C:
    pass

c = C()
c.name = 'oliver'
c.hailstone_ending = [4,2,1]

那么在背后,这些类的成员查找方式是一样的吗?看起来,A 只需要一个字典就能查找所有实例的成员;而 C 则需要在每个实例中使用一个字典。如果解释器非常聪明,它理论上可以发现所有 B 的实例都必须包含成员 namehailstone_ending,因此它可以把这看作和 A 是一样的。

但另一方面,如果允许在查找类成员的字典上使用 del 操作,那么这些类的查找机制可能是相同的,因为可用的成员会依赖于实例。

我感兴趣的是,我之前有一些代码创建了几千个 C 类型的类,我发现它运行得非常慢,而且占用内存很大。最近我用不同的方式重写了这段代码,感觉更高效了(但我还没有严格测试过,所以可能还是一样的)。

非常感谢你的见解!

2 个回答

2

在Python中,每个类和每个实例都有一个字典。A使用的是类字典,而类BC的例子则使用的是实例字典。BA是不一样的——Python并不是为了速度而设计的,证明B的实例不会被改变是非常困难,甚至几乎不可能。

证明:

>>> class D:
...   def __init__(self):
...     self.a = 3
... 
>>> d = D()
>>> d.a
3
>>> D.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class D has no attribute 'a'
>>> dd = D()
>>> dd.a
3
>>> D.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class D has no attribute 'a'

你的问题让我想到了JavaScript的原型。

另外,如果在实例字典中找不到某个成员,查找时可以回退到类字典,但写入时会使用实例字典。

2

在底层,类的成员查找方式是一样的吗?

除非你重写了 __getattribute__,否则属性查找的顺序是先检查对象的属性,然后再检查类的属性。它并不关心这个类是怎么来的。

看起来,A 只需要一个字典就能查找所有实例的成员;而 C 则需要在每个实例中使用一个字典。如果解释器足够聪明,它理论上可以注意到 B 的所有实例都必须包含成员 name 和 hailstone_ending,因此它可以和 A 等价。

A 中,属性存储在 A.__dict__ 中,也就是在类本身上。而在 BC 中,属性存储在 self.__dict__ 中,也就是在实例上。这两个字典在所有情况下都是存在的。就这么简单。而且,BC 之间没有区别。

撰写回答