为什么要将属性添加到已经实例化的对象中?

我正在学习python,尽pipe我认为我理解了Python的全部概念和概念,但是今天我偶然发现了一些我并没有完全理解的代码:

说我有一个class级,应该定义圈子,但缺乏一个身体:

class Circle(): pass 

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

 my_circle = Circle() my_circle.radius = 12 

奇怪的部分是Python接受上述语句。 我不明白为什么Python不会提出一个undefined name error 。 我明白,通过dynamictypes,我只是将variables绑定到对象,只要我想,但不应该在Circle类中存在一个属性radius允许我这样做?

编辑 :在你的答案很多精彩的信息! 谢谢大家的所有这些梦幻般的答案! 很可惜,我只能把其中一个标记为答案。

一个主要的原则就是没有声明这样的东西 。 也就是说,你永远不会声明“这个类有一个方法富”或“这个类的实例有一个属性栏”,更不用说在这里存储的对象types的声明。 你只需定义一个方法,属性,类等,并添加它。 正如JBernardo所指出的,任何__init__方法都是一样的。 任意限制创build具有名称__init__方法的新属性是没有什么意义的。 把函数存储为__init__实际上并没有那个名字(例如装饰器),这样做有时会很有用,而这样的限制会打破这个限制。

现在,这不是普遍的事实。 Builtintypes省略了这个function作为优化。 通过__slots__ ,你也可以防止用户定义的类。 但这仅仅是一个空间优化(不需要每个对象的字典),而不是一个正确的事情。

如果你想要一个安全网,太糟糕了。 Python并没有提供它,你不能合理地添加一个,最重要的是,Python程序员会接受这种语言(阅读:几乎所有你想要的)。 testing和纪律,仍然有很长的路要确保正确性。 如果可以避免的话 ,不要使用自由来构成__init__以外的属性,并进行自动化testing。 由于这样的技巧,我很less有一个AttributeError或者一个逻辑错误,而那些发生的事情几乎全部都被testing所捕获。

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

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

而这个代码:

 class Foo(object): pass foo = Foo() foo.bar = 5 

完全相同 。 真的没有区别。 它完全一样的东西。 这种区别在于,在第一种情况下,它是封装的,很显然bar属性是Footypes对象的正常部分。 在第二种情况下,这是不明确的。

在第一种情况下,你不能创build一个没有bar属性的Foo对象(你可能可以,但不是很容易),在第二种情况下,除非你设置Foo对象,否则Foo对象不会有bar属性。

因此,虽然代码在编程上是等价的,但在不同的情况下使用它。

Python允许您在任何实例(或类)上存储任何名称的属性。 可以通过用C编写类,就像内buildtypes一样,或者使用只允许某些名字的__slots__来阻塞。

它的工作原理是大多数实例将其属性存储在字典中。 是的,像您定义的Python字典一样使用{} 。 字典存储在名为__dict__的实例属性中。 事实上,有人说“class级只是词典的语法糖”。 也就是说,你可以做一切你可以用一个字典做一个类, 类只是让它更容易。

你习惯于在编译时必须定义所有属性的静态语言。 在Python中,类定义被执行 ,而不是编译; 类与其他类一样是对象; 并且添加属性与将项目添加到字典一样简单。 这就是为什么Python被认为是一种dynamic语言。

不,python是灵活的,它不强制你可以存储在用户定义的类上的属性。

然而,在类定义中使用__slots__属性将会阻止您创build__slots__序列中未定义的附加属性:

 >>> class Foo(object): ... __slots__ = () ... >>> f = Foo() >>> f.bar = 'spam' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Foo' object has no attribute 'bar' >>> class Foo(object): ... __slots__ = ('bar',) ... >>> f = Foo() >>> f.bar Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: bar >>> f.bar = 'spam' 

它创build一个my_circleradius数据成员。

如果你问它my_circle.radius它会引发一个exception:

 >>> print my_circle.radius # AttributeError 

有趣的是,这不会改变这个class级。 只是一个例子。 所以:

 >>> my_circle = Circle() >>> my_circle.radius = 5 >>> my_other_circle = Circle() >>> print my_other_circle.radius # AttributeError 

Python中有两种types的属性 – Class Data AttributesInstance Data Attributes

Python为您提供了dynamic创buildData Attributes灵活性。

由于实例数据属性与实例相关,因此您也可以在__init__方法中执行此操作,或者可以在创build实例后执行此操作。

 class Demo(object): classAttr = 30 def __init__(self): self.inInit = 10 demo = Demo() demo.outInit = 20 Demo.new_class_attr = 45; # You can also create class attribute here. print demo.classAttr # Can access it del demo.classAttr # Cannot do this.. Should delete only through class demo.classAttr = 67 # creates an instance attribute for this instance. del demo.classAttr # Now OK. print Demo.classAttr 

所以,你可以看到我们已经创build了两个实例属性,一个在__init__里面,一个在实例创build之后。

但是区别在于,在__init__创build的实例属性将为所有实例设置,而如果在外部创build,则可以为不同的实例设置不同的实例属性。

这与Java不同,一个类的每个实例都具有相同的一组实例variables。

  • 注意:虽然你可以通过一个实例访问一个类属性,但是你不能删除它。另外,如果你试图通过一个实例修改一个类属性,你实际上会创build一个影响类属性的实例属性。