Name and Bind: What does "=" do
In a short word, Python doesn't have real variable. The variable you see is actually a name(like the alias in other languages). And the operator =
, which is always called Assignment, bind the name to an object. (In Python, everything is an object)
For example:
x = 3
The =
doesn't really change the value of x
, coz actually there is no variable x
contain a value.
Instead, it creates a immutable object 3
and makes x
a name bind to it.(similar to C++'s reference)
So, if we do
>>> a = [1,2]
>>> b = a
>>> print(id(a)) # id(object) will return the address of object in memory
2426261961288
>>> print(id(b))
2426261961288
>>> a is b # operator "is" evaluate whether a and b refer to the same object.
True
>>> b.append(3)
>>> print(id(b)) # b's address didn't change
2426261961288
>>> print(a)
[1, 2, 3]
>>> print(b)
[1, 2, 3]
First, a = [1,2]
binds the name a
to an mutable object, which is a list [1, 2]
.(For better understanding, I would annotate this underlying object a nickname OBJ_288)
Then, b = a
binds b
to the same object which a
reference to, OBJ_288.
You can see, id(a)
is the same as id(b)
, which means their addresses are the same.
b.append(3)
actually change the object b
is bound to(as b.append
refers to a method of OBJ_288). Now OBJ_288 becomes [1, 2, 3]
, to whom a
and b
are bound to.
So when we print(a)
and print(b)
, the results are the same.
However, if we do
>>> b = [4, 5, 6]
>>> a is b
False
>>> id(a)
2426261961288
>>> id(b)
2426262048840
>>> print(a)
[1, 2, 3]
When we call operator=
for b
, b
will bind to another object (here is the new object we created by [4, 5, 6], let's nickname it OBJ840)
While a
still refers to OBJ_288, print(a)
is still [1, 2, 3]
For detail, please see the following references (If you have knowledge of C++, you could understand the first 2 references easier):
https://realpython.com/pointers-in-python/#names-in-python
https://eev.ee/blog/2012/05/23/python-faq-passing/
Also, the detailed rules are stated in Python Official Reference.
https://docs.python.org/3/reference/executionmodel.html#naming-and-binding
Scope, Class attribute and Instance attribute
In your code:
class A:
arr = []
>>> a1, a2 = A(), A()
>>> a1.arr.append(0)
a1
is an instance of class A
, and in Python, for attribute name resolution, every instance creates a namespace, which is the first place in which attributes references are searched, if not found, it will continue searching in its class namespace(where the class attributes are).
So in your case, since a1
has no instance attr named arr
, it refers to class A's attribute A.arr
. and append
is A.arr
's method, which would modify A.arr
, which results:
>>> A.arr
[0]
But if you do
>>> a1.arr = [1,2,3]
Remember I said in Name and Bind: What does "=" do, the = assignment would binds its left hand side name to its right hand side object.
Also, in Python, assignments to names always go into the innermost scope(except specified by global
or nonlocal
). Here it means, it will bind object [1,2,3] to a1.arr
, which is a1
's instance attribute, even it doesn't exist before.
Now a1
has a new instance attribute arr
, so a1.arr
, as the attribute name resolution rule, it will shadow A.arr
. That's why:
>>> a1.arr
[1, 2, 3]
And class A's class attribute A.arr
is not affected.
>>> a2.arr
[0]
>>> A.arr
[0]
Reference:
https://docs.pythonorg/3/reference/datamodel.html#the-standard-type-hierarchy item Class instances
https://docs.python.org/3/tutorial/classes.html#a-word-about-names-and-objects