繁体   English   中英

为什么当你设置一个不存在的类的属性时,python 不会抛出异常

[英]Why doesn't python throw an exception when you set an attribute of a class that doesn't exist

我一直试图调试我的代码,结果发现这是我错误的原因,很难找到。 一个简单的例子来演示我在说什么:

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__可以添加到该命名空间,但其他任何人也可以(现代 CPython 有一些优化来减少内存使用,如果你只在__init__创建相同的属性集并且不再创建更多,但它会回退如果您违反该规则以保留现有行为,则转移到旧的、内存密集型的存储)。

这有时很方便,例如,当类上的另一个方法仅在调用该方法并且需要计算时才想要懒惰地计算属性。 它只是让属性未定义,在需要它的地方,它捕获AttributeError并计算(并缓存)当时的值。

这是高级脚本类语言中非常常见的设计(除了 Python,其他默认允许这样做的语言包括 Perl、Ruby 和 JavaScript,仅举几例),因为它们对类实例的基本定义只是“一个字符串键控dict ,上面有一些魔法”。 虽然他们可以制定规则使事情变得更加严格,但这样做并没有什么好处,所以他们只是让事情尽可能灵活。

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

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

并尝试分配给b属性(在类内部或外部)将因:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM