2

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
0

3 Answers 3

6

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.

Sign up to request clarification or add additional context in comments.

5 Comments

I understood about the example 3 and self. self works as an actual object of the class we define so there is no any object at this point. However, I don't get the difference between first two examples. I apparently can change the content of integer because print a.foo = 5 in the first example. Can you clarify a bit more?
@Konstantin: That isn't changing the integer value, it's changing what the variable points to. See this page for a simple explanation of what variables are and aren't in Python.
"The reason they seem different is because you can change the contents of a list, but you can't change the contents of an integer." No. The reasin they are different is that 1) involves a re-assignment and 2) an inplace change (which, admittedly, is only possible with mutable objects). You make that clear in the next paragraph, but the first one can be a little bit misleading.
@glglgl: You're right, and that's actually a pet peeve of mine too. I edited the answer to make it clearer.
@BrenBarn Now, absolutely perfect. (Except that you could condense paragraph 1 and 2.) I'd give you another +1 if I could :-)
1

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.

2 Comments

Aaron, very nice explanation! Struggling a bit since I started doing some stuff using Python instead of C/C++
@Konstantin Thanks for the recognition. :) I was going to leave Bren to it, but I figured I had something to complement him.
1

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.

1 Comment

Great point about "fallback" attribute in Example 1, so it acts like static member of a class and as a default attribute of instances at the same time. When you change it as a static variable of the class like A.foo = 7 the default attribute changes

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.