我学习蟒蛇,虽然我觉得我得到的整体概念和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 Attributes
和Instance 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?