Reputation: 3159
Why do the examples below behave differently?
Example 1: foo
seems to behave like a class variable that is specific for various objects
class A:
foo = 1
a, b = A(), A()
a.foo = 5
print b.foo
----------------
Output: 1
Example 2: foo
seems to behave like a static class variable that is the same for all object. Perhaps the behavior has something to do with lists working as pointers.
class A:
foo = []
a, b = A(), A()
a.foo.append(5)
print b.foo
----------------
Output: [5]
Example 3: Doesn't work
class A:
self.foo = []
a, b = A(), A()
a.foo.append(5)
print b.foo
----------------
Output: Error
Upvotes: 2
Views: 1677
Reputation: 251383
The first two examples are both class attributes. The reason they seem different is because you're not doing the same thing in both cases: you're assigning a new value in the first case and modifying the existing value in the second case.
Notice that you are not doing the same thing in the first two examples. In the first example you do a.foo = 5
, assigning a new value. In the second example, if you did the analogous thing, assigning, a.foo = [5]
, you would see the same kind of result as in the first example. But instead you altered the existing list with a.foo.append(5)
, so the behavior is different. a.foo = 5
changes only the variable (i.e., what value it points to); a.foo.append(5)
changes the value itself.
(Notice that there is no way to do the equivalent of the second example in the first example. That is, there's nothing like a.foo.add(1)
to add 1 to 5. That's because integers are not mutable but lists are. But what matters is not that lists "are" mutable, but that you mutated one. In other words, it doesn't matter what you can do with a list, it matters what you actually do in the specific code.)
Also, notice that although the foo
you defined in the class definition is a class attribute, when you do a.foo = 5
, you are creating a new attribute on the instance. It happens to have the same name as the class attribute, but it doesn't change the value of the class attribute, which is what b.foo
still sees.
The last example doesn't work because, just like in the first two examples, code inside the class
block is at the class scope. There is no self
because there are no instances yet at the time the class is defined.
There are many, many other questions about this on StackOverflow and I urge you to search and read a bunch of them to gain a fuller understanding of how this works.
Upvotes: 6
Reputation: 91017
The difference has not to do with mutability/immutability, but what operations are performed.
In example 1, the class has an attribute foo
. After object creation, you give the object another attribute foo
which shadows the former one. So the class attribute acts as a kind of "default" or "fallback".
In example 2, you have one object which you perform an operation on (which, admittedly, only works on mutable objects). So the object referred to by A.foo
, which can be accessed as well via a.foo
and b.foo
due to the lack of an instance attribute with the same name, gets added a 5
.
Example 3 doesn't work because self
doesn't exist where you use it.
Note that example 1 would as well work with mutable objects, such as lists:
class A:
foo = []
a, b = A(), A()
a.foo = []
a.foo.append(5)
b.foo.append(10)
print a.foo # [5]
print b.foo # [10]
print A.foo # [10]
Here a.foo
gets a new, empty list. b.foo
, lacking an instance attribute, continues to refer to the class attribute. So we have two empty lists which are independent of each other, as we see when .append()
ing.
Upvotes: 1
Reputation: 394995
This doesn't work:
class A:
self.foo = []
Which raises an error.
NameError: name 'self' is not defined
Because self
is not a keyword in Python, it's just a variable name commonly assigned to the instance of the class that is passed to a method of the class when the class is called.
Here's an example:
class A(object):
def __init__(self):
self.foo = []
a, b = A(), A()
a.foo.append(5)
print(b.foo)
Then returns:
[]
When each one is initialized, they each get their own list which can be accessed by the attribute foo
, and when one is modified, the other, being a separate list stored at a different place in memory, is not affected.
Upvotes: 1