为什么 python 在设置不存在的类的属性时不抛出异常



我试图永远调试我的代码,结果发现这就是我的错误的原因,让它很难找到。一个简单的例子来演示我所说的:

class Test():
def __init__(self):
self.a = 0
x = Test()
x.b = 2
print(x.a)
print(x.b)

此代码没有引发任何错误。事实上,它将成功打印出0和2。因此,即使Test不包含实例变量b,它仍然会在我分配它时创建它

y = Test()
print(y.b)

它将按预期抛出一个错误。

那么,为什么首先要有这个功能,以便能够在类的实例上创建一个新属性呢?是什么在幕后促成了这种行为?有没有什么方法可以让我禁用这种行为,或者至少在编程时以某种方式捕捉它?

标准Python类在引擎盖下的dict(名为__dict__(上存储实例属性。没有任何特殊规则可以让它在__init__内的赋值和其他任何地方的赋值之间做出有意义的区分;在__init__之前,它是一个空的命名空间,__init__可以添加到该命名空间中,但其他任何人也可以(如果您只在__init__中创建相同的属性集,而从不创建更多属性,那么现代CPython可以进行一些优化,以减少内存使用,但如果您违反该规则来保留现有行为,它将回到旧的、更占用内存的存储中(。

这有时很方便,例如,当类上的另一个方法只想在调用该方法并且需要计算的情况下延迟计算属性时。它只保留未定义的属性,在需要它的地方,它捕获AttributeError并计算(并缓存(当时的值。

这是高级脚本类语言中非常常见的设计(除了Python之外,默认情况下允许这样做的其他语言包括Perl、Ruby和JavaScript,仅举几个例子(,因为它们对类实例的基本定义只是";一个字符串键控的CCD_ 9,在它上面有一些魔力";。虽然他们可以制定规则,使事情更具限制性,但这几乎没有什么好处,所以他们只是让事情尽可能灵活。

正如您所注意到的,像这样的属性的自动生动化可能会变得混乱,有时这是不可取的。如果这是一个问题,并且您想要预先定义一组可以定义的受限属性,那么只需在类本身上使用有效属性的字符串名称定义__slots__即可。这将用底层属性数组中连续分配的插槽替换属性的底层dict(插槽名称成为知道如何唯一访问每个插槽的描述符(。它通过避免每个实例相对浪费的dict来节省内存,并将防止创建新属性。对于你的情况,你只需要做:

class Test():
__slots__ = 'a',
def __init__(self):
self.a = 0

并且尝试分配给b属性(在类内或类外(将与一起失效

AttributeError: 'Test' object has no attribute 'b'

请注意,这也禁止对类的实例进行弱引用;如果您想允许的话,您必须显式地将'__weakref__'列为类中的一个插槽。

最新更新