为什么我不能在Python中更改类的属性

时间:2015-11-17 04:01:02

标签: python class object immutability mutable

我们说classes在Python中是可变的,这意味着您可以使用引用我们可以更改将在对象中反映的值。例如,

>>> A = [1, 2, 3]
>>> B = A
>>> B[2] = 5
>>> A
[1, 2, 5]

在这里,我可以使用A更改B对象的值,因为list是一种可变类型。我的问题是为什么我不能使用相同的概念改变下面一个类的属性:

class C:

    apple = 2

    def __init__(self):
        self.dangerous = 2

D = C # D is pointing to same class C

D().dangerous = 5 # changing the value of class attribute D

D().apple = 3 # changing the value of apple here

print D().apple

print D().dangerous

OUTPUT:
2
2

有人可以解释为什么输出为22但不是35,因为我们说该类是mutable类型。

更新:请参阅@ zxq9的答案,如果您在执行D=C时看到下图,D实际上指的是同一个类,而不是您所描述的新对象。你能解释一下这个:

enter image description here

2 个答案:

答案 0 :(得分:10)

每次在课后放置parens时,您都在构建类的新实例对象。所以你打印的东西是新品牌,并没有反映你之前做过的短暂作业。

以下是一个示例(扩展为涵盖对C类的基础引用):

>>> class C:
...   red = 2
...   def __init__(self):
...     self.blue = 2
... 
>>> C.red
2
>>> C.blue
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'C' has no attribute 'blue'
>>> C().red
2
>>> C().blue
2
>>> #OOOOH!
... 
>>> z = C()
>>> z.red
2
>>> z.blue
2
>>> D = C
>>> D.red
2
>>> D().red
2
>>> D().red = "over 9000!"
>>> D.red
2
>>> D.red = "No, really over 9000!"
>>> D.red
'No, really over 9000!'
>>> C.red
'No, really over 9000!'
>>> #OOOOOOHHHH!
...

请注意,当我分配D.red = "No, really over 9000!"时,我们 直接更改了类 - 因为它引用了类定义本身,而不是从中创建的实例化对象它。另请注意,分配D(副本)的属性会更改C(原始)的属性,因为在许多(但不是全部)情况下,Python通过引用进行此类分配,这意味着D实际上是C的别名,而不是底层结构的副本。阅读Python的deepcopy()方法,了解有关特别令人吃惊的细节的更多信息。

仔细阅读示例代码,注意引用 ClassName调用 ClassName()之间的区别。第一个是通过变量名称的引用到类定义 - 用于生成带有构造函数__init__()的实例对象的蓝图。第二个是__init__() invokation ,其返回值是定义它的类的实例对象。

这也是你可以做这样的事情的原因:

def some_fun(another_fun, value):
    another_fun(value)

def foo(v):
    return v + v

def bar(v):
    return v * v

some_fun(foo, 5)
some_fun(bar, 5)

此功能为Python在构建功能抽象方面提供了高度的灵活性。 (现在,如果它只有尾声消除......)

答案 1 :(得分:0)

这是一个有趣的例子。

  1. D()。dangerous = 5 行将更改 D()实例的“危险”属性;但打印D()。dangerous 这一行打印出另一个实例 D()的属性“危险”。
  2. D()。apple = 3 在实例 D()中创建属性“apple”,因为此实例的确如此没有属性“apple”。
  3. 打印D()。apple 这一行将打印出类 D 的属性“apple”,因为实例 D()没有属性“apple”。
  4. 通过实例更改类的属性“apple”的一种方法是使用 D().__ class __。apple = 3