为什么允许向已实例化的对象添加属性?

新手上路,请多包涵

我正在学习 python,虽然我认为我了解了 Python 的整个概念和概念,但今天我偶然发现了一段我没有完全理解的代码:

假设我有一个应该定义 Circles 但缺少主体的类:

 class Circle():
    pass

由于我没有定义任何属性,我该怎么做:

 my_circle = Circle()
my_circle.radius = 12

奇怪的是 Python 接受了上面的语句。我不明白为什么 Python 不引发 undefined name error 。我知道通过 动态类型 我只是在需要时将变量绑定到对象,但是属性 radius 不应该存在于 Circle 类中以允许我这样做吗?

编辑:你的答案中有很多精彩的信息! 谢谢大家所有这些精彩的答案! 很遗憾我只能标记一个作为答案。

原文由 NlightNFotis 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 503
2 个回答

一个主要原则是 _没有声明这样的东西_。也就是说,你永远不会声明“这个类有一个方法 foo”或“这个类的实例有一个属性 bar”,更不用说声明要存储在那里的对象的类型了。您只需定义一个方法、属性、类等,然后将其添加。正如 JBernardo 指出的那样,任何 __init__ 方法都做同样的事情。任意将新属性的创建限制为名称为 __init__ 的方法没有多大意义。有时将函数存储为 __init__ 是有用的,它实际上没有那个名称(例如装饰器),这样的限制会破坏它。

现在,这不是普遍正确的。作为优化,内置类型忽略了此功能。通过 __slots__ ,您还可以在用户定义的类上防止这种情况。但这仅仅是空间优化(不需要每个对象的字典),而不是正确性。

如果你想要一个安全网,那就太糟糕了。 Python 不提供,您也无法合理地添加一个,最重要的是,拥抱该语言的 Python 程序员会回避它(阅读:几乎所有您想与之合作的人)。测试和纪律对于确保正确性还有很长的路要走。不要随意在 __init__ 之外编造属性, 如果可以避免 的话,做自动化测试。我很少有 AttributeError 或由于像这样的诡计而导致的逻辑错误,并且在发生的那些中,几乎所有都被测试捕获。

原文由 user395760 发布,翻译遵循 CC BY-SA 3.0 许可协议

只是为了澄清这里讨论中的一些误解。这段代码:

 class Foo(object):
    def __init__(self, bar):
        self.bar = bar

foo = Foo(5)

这段代码:

 class Foo(object):
    pass

foo = Foo()
foo.bar = 5

完全等价 的。真的没有区别。它做完全一样的事情。这种区别在于,在第一种情况下它被封装了,很明显 bar 属性是 Foo 类型对象的正常部分。在第二种情况下,情况是否如此尚不清楚。

在第一种情况下,您不能创建没有 bar 属性的 Foo 对象(好吧,您可能可以,但并不容易),在第二种情况下,除非您设置 Foo 对象,否则 Foo 对象将没有 bar 属性。

因此,尽管代码在编程上是等效的,但它用于不同的情况。

原文由 Lennart Regebro 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题