I was learning this and this to understand class attributes. But got confused with the output of following code snippet.
class A:
aliases = None
name = None
def __init__(self,name):
self.name = name
self.aliases = set([name])
def add_aliases(self,a):
self.aliases.add(a)
def __repr__(self):
return str(self.name) + str(self.aliases)
arr = []
for i in range(3):
arr.append(A(i))
arr[-1].add_aliases(i+1)
for item in arr:
print item
A.aliases = set([]) ##Modify the static element of class
for item in arr:
print item
Python Interpreter: 2.7.9
And output is
0set([0, 1])
1set([1, 2])
2set([2, 3])
0set([0, 1])
1set([1, 2])
2set([2, 3])
And I was expecting something like this as an output.
0set([2, 3])
1set([2, 3])
2set([2, 3])
0set([])
1set([])
2set([])
And the explanation is that when we write self.aliases = set([])
we are actually creating a new instance attribute, shadowing the class attribute.
So, if we make our __init__
function as follows we get the expected output.
def __init__(self,name):
self.name = name
A.aliases = set([name]) #using the class attribute directly
Also consider following code snippet:
class A:
aliases = set([])
name = None
def __init__(self,name):
self.name = name
self.aliases.add(name) # using the class attribute indirectly.
def add_aliases(self,a):
self.aliases.add(a)
def __repr__(self):
return str(self.name) + str(self.aliases)
Since in this case, we're not creating an instance attribute, there is no shadowing. And the test code in the question, would produce this output:
0set([0, 1, 2, 3])
1set([0, 1, 2, 3])
2set([0, 1, 2, 3])
0set([])
1set([])
2set([])
which is expected, as the class attributes are shared across all instances.
Here A.alias
can also be referred as self.alias
inside init
or any other function. And since it did not shadow the static attribute, gave the expected output -- the case when all the the object share a common attribute.
A person not aware of this concept will not notice anything while using immutable data structure like string
etc. But in case of data-structures like list
and dictionary
this may surprise.
Also consider following definition of init
.
def __init__(self,name):
self.name = name
self.aliases.add(name) # referring to class attribute
self.aliases = set([]) # creating a instance attribute
And this case also, it ended up creating instance attribute
and output produced by test code is:
0set([1])
1set([2])
2set([3])
0set([1])
1set([2])
2set([3])
And form all this my learning is:
Always refer class attribute with class name and instance attribute with object name, i.e. write A.aliases
when you mean class attribute aliases
, do not write self.aliases
to indirectly refer self.aliases
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With