为什么添加属性已经实例化对象允许吗?(Why is adding attributes to an

2019-06-18 06:56发布

我学习蟒蛇,虽然我觉得我得到的整体概念和Python的概念,今天我偶然发现了一段代码,我没有完全理解:

说我有一个应该定义的社交圈,但缺乏一个体类:

class Circle():
    pass

因为我还没有定义任何属性,我怎么可以这样做:

my_circle = Circle()
my_circle.radius = 12

怪异的是,Python的接受上述声明。 我不明白为什么Python不引发undefined name error 。 我也明白,通过动态类型我只是绑定变量的对象,每当我想要的,但应该不是一个属性radius的存在, Circle类,让我做到这一点?

编辑 :在你的答案的精彩信息很多! 谢谢大家对所有那些美妙的答案! 很遗憾,我只得到一个标记作为答案。

Answer 1:

一个领先的原则有没有这样的事,作为一个声明 。 也就是说,你从来没有宣布“这个类有一个方法foo”或“这个类的实例有一个属性栏”,更别说拍一部关于被存储在那里的对象类型的声明。 您只需定义一个方法,属性,类等,它的加入。 作为JBernardo指出,任何__init__方法做同样的事情。 它不会让一个很有意义任意限制名为方法的新属性创建__init__ 。 因此有时候有用的存储功能__init__这实际上不具有名称(例如装饰),这样的限制将打破。

现在,这并不是普遍适用的。 内建类型省略此能力作为优化。 通过__slots__ ,你也可以防止这种在用户定义的类。 但是,这仅仅是一个优化空间(无需字典为每个对象),而不是正确的事情。

如果你想有一个安全网,好了,太糟糕了。 Python不提供一个,你不能合理地添加一个,也是最重要的是,它会由谁拥抱语言Python程序员避而远之(读:几乎你所要使用的)。 测试和纪律,仍然很长的路要走确保正确性。 不要用自由来弥补属性外__init__ 如果能够避免它 ,做自动化测试。 我很少有一个AttributeError或逻辑错误,由于这样的欺骗,而那些发生的,几乎所有的被测试捕获。



Answer 2:

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

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

foo = Foo(5)

而这种代码:

class Foo(object):
    pass

foo = Foo()
foo.bar = 5

完全等价的 。 真的是没有什么区别。 它不完全一样的东西。 此不同的是,在第一种情况下它的封装并且很显然,杆属性是富类型对象的正常部分。 在第二种情况下,目前尚不清楚,这是如此。

在第一种情况下,你不能创建一个没有酒吧属性的Foo对象(当然,你可能可以,但是不容易),在第二种情况下,除非您设置的美孚对象不会有一个酒吧属性。

因此,尽管代码编程等同,它在不同的情况下使用。



Answer 3:

利用Python可以存储在几乎任何情况下的任何名称的属性(或类,对于这个问题)。 这是可能的在写C类,像禁止此内置类型,或使用__slots__只允许特定的名称。

它的工作原理的原因是,大多数情况下,存储及其属性的字典。 是的,像你这样的普通Python字典会与定义{} 字典存储在名为实例属性__dict__ 。 事实上,有人说“类的字典只是语法糖。” 也就是说,你可以做一切你可以用字典的类来完成; 班只是使它更容易。

你已经习惯了静态语言,你必须定义在编译时的所有属性。 在Python中,类定义执行 ,没有编制; 类对象,就像任何其他的; 和添加属性是作为将项目添加到词典一样简单。 这就是为什么Python中被认为是一种动态语言。



Answer 4:

没有,蟒蛇是灵活的那样,它不执行什么属性,你可以在用户定义的类存储。

还有一招然而,使用__slots__属性的一类定义会阻止您创建的没有定义的附加属性__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'


Answer 5:

它创建了一个radius的数据成员my_circle

如果你问它my_circle.radius它会抛出一个异常:

>>> print my_circle.radius # AttributeError

有趣的是,这并没有改变类; 只是一个实例。 所以:

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


Answer 6:

有两种类型的Python属性- Class Data AttributesInstance Data Attributes

Python的给你创造的灵活性, Data Attributes的飞行。

由于实例数据属性与实例,你也可以做,在__init__方法,或者你可以做到这一点你创建实例之后..

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  

所以,你看,我们已经创建了两个实例属性,一个内部__init__和一个外,在创建实例后..

但不同的是,内部创建实例属性__init__将所有实例进行设置,而如果创建外,你可以有不同的实例不同isntances属性..

这是Java不同,其中一类的每个实例具有相同的一组实例变量..

  • 注: - 虽然你可以通过一个实例访问类的属性,你不能删除它..此外,如果你试图通过一个实例来修改类的属性,你居然创造出阴影的类属性的实例属性..


Answer 7:

作为delnan说,你能获得与这种行为__slots__属性。 但事实上,它是一种方式,以节省存储空间和访问类型不抛弃的事实,这是(也)A /平均禁用动态属性。

禁用动态属性是做,如果只是为了防止微妙的错误是由于拼写错误一件合理的事。 “测试和纪律”是不错,但依靠自动验证肯定是没有错或者 - 而不一定unpythonic无论是。

此外,由于attrs库在2016年达到了16版(原题和答案后明显的方式),建立一个封闭的类插槽从未如此简单。

>>> import attr
...
... @attr.s(slots=True)
... class Circle:
...   radius = attr.ib()
...
... f = Circle(radius=2)
... f.color = 'red'
AttributeError: 'Circle' object has no attribute 'color'


文章来源: Why is adding attributes to an already instantiated object allowed?