简体   繁体   中英

Why can't I change attribute of a class in Python

We say classes are mutable in Python which means you can using references we can change the values that will be reflected in object. For example,

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

Here I can change the values of A object using B because list is a mutable type. My question is why can't I change the attributes of a class below using same concept:

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

Could anyone explain why the output is 2 and 2 but not 3 and 5 since we are saying that the class is a mutable type.

UPDATE : Referring to the answer by @zxq9, if you see the below diagram when do D=C , D is actually pointing to the same class rather a new object as you have described. Could you explain this:

在此输入图像描述

Each time you place parens after a class, you are constructing a new instance object of the class . So the things you printed were brand-spanking new and did not reflect the short-lived assignments you had made previously.

Here is an example (expanded to cover the underlying reference to class 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!
...

Note that we did change the class directly when I assigned D.red = "No, really over 9000!" -- because that was referencing the class definition itself , not an instantiated object created from it. Note also that assigning an attribute of D (a copy) changed the attribute of C (the original) because in many (but not all) cases Python makes such assignments by reference , meaning that D is really an alias of C, not copy of the underlying structure. Read up on Python's deepcopy() method for more about that particularly startling detail.

Walk through the example code carefully, note the difference between referencing ClassName and calling ClassName() . The first is a reference via a variable name to a class definition -- a blueprint for generating instance objects that carries a constructor function __init__() with it. The second is an invokation of __init__() whose return value is an instance object of the class within which it is defined.

This is also why you can do things like this:

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)

This feature lends Python a high degree of flexibility in building functional abstractions. (Now if only it had tail-call elimination...)

It is an interesting example.

  1. The line D().dangerous = 5 will change the attribute "dangerous" of the instance D() ; But the line print D().dangerous print out the attribute "dangerous" of ANOTHER instance D() .
  2. The line D().apple = 3 will create an attribute "apple" in the instance D() since this instance does not have the attribute "apple".
  3. The line print D().apple will print out the attribute "apple" of the class D since the instance D() does not have the attribute "apple".
  4. One way to change the attribute "apple" of the class through its instance is by using D().__class__.apple=3

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM