Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are the attributes in a Python class shared or not? [duplicate]

Tags:

python

list

class

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?

like image 208
Yicun Zhen Avatar asked Jul 24 '17 15:07

Yicun Zhen


2 Answers

The difference is that in your second example you are creating a new list, self.tricks, as an attribute of the object:

def __init__(self,name):
    self.name=name
    self.tricks=[name]    # <-- this is creating the new attribute for the object
    self.tricks.append(name)

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:

def __init__(self,name):
    self.name=name
    mytest.tricks=[name]    # <-- this is accesing the class attribute instead
    self.tricks.append(name)

That will output what you are actually expecting.

like image 113
carrdelling Avatar answered Nov 14 '22 18:11

carrdelling


Your first case creates a class variable, and the second creates an instance variable.

When you do refer self.foo, Python first checks for a foo element in the instance's namespace dictionary, and then checks for a foo 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:

mytest.__dict__ = {
    'name': 'test1',
    'tricks': [],  # this doesn't get updated
}

t1.__dict__ = {
    'name': 'some_passed_name'
    'tricks': ['some_passed_name'],
}
like image 25
Daniel Fackrell Avatar answered Nov 14 '22 16:11

Daniel Fackrell