0

These two class definitions are structured identically, except in the weird() class definition the attribute 'd' is a list, and in the normal() class definition its an int. I dont understand why the weird() class results in self.d = d, while this is not the case with the class normal(). Why does Python treat int and list differently in this situation?

class weird:
    def __init__(self):
        d = [1,2,3]
        self.d = d
        for x in range(3):
            d[x] = 10+x 
        print "d =", d
        print "self.d =", self.d

class normal:
    def __init__(self):
        d = 1
        self.d = d
        d = 11
        print "d =", d
        print "self.d =", self.d

And when I run the code, I get

>>> a=weird()
d = [10, 11, 12]
self.d = [10, 11, 12]
>>> b=normal()
d = 11
self.d = 1
MattyIce
  • 13
  • 2
  • "in the weird() class definition the attribute 'd' is an int, and in the normal() class definition its a list." This is the reverse of true. – Marcin Dec 17 '12 at 17:12
  • That's not the only difference either. One does an variable assignment, while the other does a number of sequence item assignments. –  Dec 17 '12 at 17:14
  • Yup, I had that backwards. I know the other does a number of assignments, but basically "I changed the value of d". Martijn cleared it up for me. – MattyIce Dec 17 '12 at 18:21

4 Answers4

7

You are confusing assignment with mutation of an object.

In one method, you assigned new values to d, changing what it points to:

d = 11

but in the other method you changed values contained in d:

d[x] = 10 + x

Note the [x] there; you are, in essence, telling python to do this:

d.__setitem__(x, 10 + x)

The value of the variable d, which points to a list, is never changed. The values in the list referred to by d, do change.

Take a look at this previous answer of mine, which goes into more detail as to what the difference is between these two statements: Python list doesn't reflect variable change.

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
2

In fact, the language is perfectly consistent here. You are observing two different behaviours because you are performing two fundamentally different operations.

The first two lines of __init__ are consistent in both classes (except for the different types of d):

class WeirdOrNormal:
    def __init__(self):
        d = ... # 1 or [1, 2, 3]
        self.d = d

At this point, both d and self.d refer to the same object.

However, after that, the two classes diverge:

  1. weird modifies the object through the reference:

    for x in range(3):
        d[x] = 10+x 
    

    Since both d and self.d refer to the same object, the change can be observed through both.

  2. normal rebinds d to point somewhere other than self.d:

    d = 11
    

    By doing this, you've broken the link between d and self.d. They are now completely independent.

If you were to change weird to also rebind the reference, just like normal does, you'd see the two classes behave consistently with one another:

class notweird:
    def __init__(self):
        d = [1,2,3]
        self.d = d
        d = [10+x for x in self.d]
        print "d =", d
        print "self.d =", self.d

a = notweird()

This prints

d = [11, 12, 13]
self.d = [1, 2, 3]
NPE
  • 486,780
  • 108
  • 951
  • 1,012
1

For docs:

Objects whose value can change are said to be mutable; objects whose value is unchangeable once they are created are called immutable. (The value of an immutable container object that contains a reference to a mutable object can change when the latter’s value is changed; however the container is still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.) An object’s mutability is determined by its type; for instance, numbers, strings and tuples are immutable, while dictionaries and lists are mutable.

In your case list is mutable, int is immutable. Nothing weird.

More about Data model.

Alexey Kachayev
  • 6,106
  • 27
  • 24
  • 1
    This isn't quite everything, though. If OP did `d = [10 + x for x in range(3)]` then `d` and `self.d` would point to different objects. – Sam Mussmann Dec 17 '12 at 17:22
0

In the first example, both d and self.d point to a mutable object. When you add ten to an element in the object you are modifying that mutable object. Because both d and self.d point to that object, they both "see" the result.

In the second example, you are reassigning d to point to a different immutable number, thus d and self.d point to two different things.

Note the difference: in one case they both point to the same value, which you are changing, and in the other case they point to two different values.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685