The following code troubles me:-
class mytest:
name="test1"
tricks=list()
def __init__(self,name):
self.name=name
#self.tricks=[name]
self.tricks.append(name)
t1=mytest("hello world")
t2=mytest("bye world")
print t1.name,t2.name
print t1.tricks,t2.tricks
The output is:-
hello world bye world
['hello world', 'bye world'] ['hello world', 'bye world']
meaning that the list tricks
is shared by the two instances t1 and t2, which has been explained in the section 9.3.5 of https://docs.python.org/3/tutorial/classes.html
However, if I execute the following code:-
class mytest:
name="test1"
tricks=list()
def __init__(self,name):
self.name=name
self.tricks=[name]
self.tricks.append(name)
t1=mytest("hello world")
t2=mytest("bye world")
x=t1.tricks
if type(x) is list:
print 'a list'
elif type(x) is tuple:
print 'a tuple'
else:
print 'neither a tuple or a list'
print t1.name,t2.name
print t1.tricks,t2.tricks
The output is the following:-
a list
hello world bye world
['hello world', 'hello world'] ['bye world', 'bye world']
Now it seems that the list tricks
is no longer shared by those two instances t1 and t2.
My question is, what are the mechanics here?
Your first case creates a class variable, and the second creates an instance variable.
When you do refer
self.foo
, Python first checks for afoo
element in the instance's namespace dictionary, and then checks for afoo
element in the class's namespace dictionary.In the first case, since you created a class variable named
tricks
with a mutable type (a list), and didn't re-assign it specifically on the method, modifications to that list are available to every instance of the class.In your second case, things are identical except that you hid the class variable with an instance variable of the same name, so from that point on, all references to
self.tricks
refer to the instance variable instead of the class variable.The second case illustrated:
The difference is that in your second example you are creating a new list, self.tricks, as an attribute of the object:
The first example works because of Python's way of resolving the names: If self.tricks cannot be found in the object (because it hasn't been created), then it tries to find it as a member of the class. Since tricks is there, then you can access it.
It may become clear to you if you try to use mytest.tricks in your second example:
That will output what you are actually expecting.
In your first example, the class
mytest
has atricks
member, shared by all its instances:In your second example, however,
mytest
instances additionnally have atricks
member:The
self.tricks = [name]
statement gives an attribute namedtricks
toself
, that is, themytest
instance. The class still has a commontricks
member.When calling
instance.tricks
, Python first looks for atricks
member ininstance.__dict__
. If it does not find any, it looks for atricks
member intype(instance).__dict__
.Therefore, instances of your first example have no
tricks
attribute, but Python will give you themytest.tricks
they all share. On the other hand, instances of your second example do have their owntricks
attribute, and Python will return this one.In the both case, you replacing the
self.name
to a new value.In the first case you're mutating the
self.tricks
list, mutating a list don't replace it. so during the whole execution you have a single list, being mutated.In the second case, the line
self.tricks=[name]
is changing the list, creating a new list object.You can easily introspect this like:
Giving:
There's a little thing to note here in this problem.
When you pass in the name and append it to the existing shared
tricks
list, it is, as you saw, shared by all the values, because it is that list.However, when you do
self.tricks=[name]
in your second example, you are erasing that objects instance ofself.tricks
and are replacing it with the list[name]
This is similar to having a parent and child class; when the child class doesn't give a different definition for an existing function, calling it calls the parent's function. But if you do, it calls the child's function.
In the first case, you didn't create a
tricks
attribute on the object scope, so Python used the one from the class; in the second case, you created a new list and associated it with the object itself, so Python used that.For a way more thorough explanation, please, take a look at: Python Class Attributes: An Overly Thorough Guide