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