1

I see a article about the immutable object.

It says when:
variable = immutable
As assign the immutable to a variable.

for example
a = b # b is a immutable
It says in this case a refers to a copy of b, not reference to b. If b is mutable, the a wiil be a reference to b

so:

a = 10
b = a
a =20
print (b) #b still is 10

but in this case:

a = 10
b = 10
a is b # return True
print id(10)
print id(a)
print id(b) # id(a) == id(b) == id(10)

if a is the copy of 10, and b is also the copy of 10, why id(a) == id(b) == id(10)?

Tanky Woo
  • 4,906
  • 9
  • 44
  • 75
  • It's not just mutability, but the difference between pointing a location within an object at a new object (by having `some_name[some_item]` on the left hand side of an `=`) and pointing a _name_ at a new object (by having just `some_name` on the left hand side of an `=`). So if you have `inner = [0]` and `outer = [inner, inner]`, if you do `outer[0] = 1` you don't change inner, and `outer` becomes `[1, [0]]`. So even though `inner` is mutable, you don't change it if you don't access a location in it by doing `outer[0][0]` for example, where the second `[0]` points inside `inner`. – agf Apr 14 '12 at 06:06

3 Answers3

6

"Simple" immutable literals (and in particular, integers between -1 and 255) are interned, which means that even when bound to different names, they will still be the same object.

>>> a = 'foo'
>>> b = 'foo'
>>> a is b
True
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
5

While that article may be correct for some languages, it's wrong for Python.

When you do any normal assignment in Python:

some_name = some_name_or_object

You aren't making a copy of anything. You're just pointing the name at the object on the right side of the assignment.

Mutability is irrelevant.

More specifically, the reason:

a = 10
b = 10
a is b

is True, is that 10 is interned -- meaning Python keeps one 10 in memory, and anything that is set to 10 points to that same 10.

If you do

a = object()
b = object()
a is b

You'll get False, but

a = object()
b = a
a is b

will still be True.

agf
  • 171,228
  • 44
  • 289
  • 238
  • 2
    Python tries to intern `int` values, but does not always succeed. Try `a = 1000` and `b = 999` followed by `b += 1`. When I tried it just now, `a` and `b` were each bound to a different `int` object with value 1000. I think the interning will always succeed for the values 0 and 1, though. – steveha Apr 14 '12 at 05:30
  • this case: `lst = [0, 1, 2] * 2`, the list [0, 1, 2] is mutable, and I change `lst[0][0] = 5`, the lst[1][0] will also change to 5, why? – Tanky Woo Apr 14 '12 at 05:31
  • 1
    @steveha It's not succeed or fail, it's -1 to 255, as Ignacio mentioned in his answer. – agf Apr 14 '12 at 05:32
  • @TankyWoo I assume you mean `[[0, 1, 2]] * 2`. Because you get a new list that has the same item in it _twice_, so changing it in either position makes the change show up in both positions. Python hasn't made a copy of the inner list, just pointed both index `0` and index `1` at it. – agf Apr 14 '12 at 05:34
  • @TankyWoo, the repetition of the list gives you two references to the same list. Try `id(lst[0])` and `id(lst[1])`. – steveha Apr 14 '12 at 05:34
  • @agf That means if the object is mutable, and it repeat some times, all the reptition will be the same(same address)? – Tanky Woo Apr 14 '12 at 08:54
  • It doesn't matter whether or not the object is mutable. Say you have a tuple (immutable) at memory address 0. It has an object in it at index 0. That object isn't actually stored inside the tuple -- it has it's own memory address at 1. The tuple has a second object in it at index 1, stored at memory address 2. You multiply the tuple by 2. It creates a new tuple at address 3 that points to address 1, address 2, address 1, and address 2. So it doesn't matter that we created a new, immutable object -- the same object at 1 is referenced at both indexes 0 and 3 in the new tuple. – agf Apr 14 '12 at 09:07
2

Because interning has already been explained, I'll only address the mutable/immutable stuff:

As assign the immutable to a variable.

When talking about what is actually happening, I wouldn't choose this wording.

We have objects (stuff that lives in memory) and means to access those objects: names (or variables), these are "bound" to an object in reference. (You could say the point to the objects)

The names/variables are independent of each other, they can happen to be bound to the same object, or to different ones. Relocating one such variable doesn't affect any others.

There is no such thing as passing by value or passing by reference. In Python, you always pass/assign "by object". When assigning or passing a variable to a function, Python never creates a copy, it always passes/assigns the very same object you already have.

Now, when you try to modify an immutable object, what happens? As already said, the object is immutable, so what happens instead is the following: Python creates a modified copy.

As for your example:

a = 10
b = a
a =20
print (b) #b still is 10

This is not related to mutability. On the first line, you bind the int object with the value 10 to the name a. On the second line, you bind the object referred to by a to the name b.

On the third line, you bind the int object with the value 20 to the name a, that does not change what the name b is bound to!

It says in this case a refers to a copy of b, not reference to b. If b is mutable, the a wiil be a reference to b

As already mentioned before, there is no such thing as references in Python. Names in Python are bound to objects. Different names (or variables) can be bound to the very same object, but there is no connection between the different names themselves. When you modify things, you modify objects, that's why all other names that are bound to that object "see the changes", well they're bound to the same object that you've modified, right?

If you bind a name to a different object, that's just what happens. There's no magic done to the other names, they stay just the way they are.

As for the example with lists:

In [1]: smalllist = [0, 1, 2] 
In [2]: biglist = [smalllist]    
In [3]: biglist
Out[3]: [[0, 1, 2]] 

Instead of In[1] and In[2], I might have written:

In [1]: biglist = [[0, 1, 2]]
In [2]: smalllist = biglist[0]

This is equivalent.

The important thing to see here, is that biglist is a list with one item. This one item is, of course, an object. The fact that it is a list does not conjure up some magic, it's just a simple object that happens to be a list, that we have attached to the name smalllist.

So, accessing biglist[i] is exactly the same as accessing smalllist, because they are the same object. We never made a copy, we passed the object.

In [14]: smalllist is biglist[0]
Out[14]: True

Because lists are mutable, we can change smallist, and see the change reflected in biglist. Why? Because we actually modified the object referred to by smallist. We still have the same object (apart from the fact that it's changed). But biglist will "see" that change because as its first item, it references that very same object.

In [4]: smalllist[0] = 3
In [5]: biglist
Out[5]: [[3, 1, 2]]

The same is true when we "double" the list:

In [11]: biglist *= 2
In [12]: biglist
Out[12]: [[0, 1, 2], [0, 1, 2]]

What happens is this: We have a list: [object1, object2, object3] (this is a general example) What we get is: [object1, object2, object3, object1, object2, object3]: It will just insert (i.e. modify "biglist") all of the items at the end of the list. Again, we insert objects, we do not magically create copies.

So when we now change an item inside the first item of biglist:

In [20]: biglist[0][0]=3
In [21]: biglist
Out[21]: [[3, 1, 2], [3, 1, 2]]

We could also just have changed smalllist, because for all intents and purposes, biglist could be represented as: [smalllist, smalllist] -- it contains the very same object twice.

phant0m
  • 16,595
  • 5
  • 50
  • 82