Reputation: 1495
I have a very basic question about accessing class variables.
I thought I can reference class variables in a member function using the self keyword or the class name.
The code block below works along with my understanding
class Coo(object):
num = 1
def foo(self):
print self.num
print Coo.num
coo = Coo()
coo.foo()
Output:
1
1
The confusion starts with below example
class Coo(object):
num = 1
def foo(self):
self.num = 2
Coo.num = 3
print self.num
print Coo.num
coo = Coo()
coo.foo()
Output:
2
3
The second example shows that accessing class variable using self or class name are different.
What would be the right way to access class variables in a member function?
Upvotes: 2
Views: 87
Reputation: 334
Python acts in a bit of a tricky way with class variables imo. Taking this snippet of code as example :
class a:
var = 0
def foo(self):
self.my_name = [k for k,v in globals().items() if v is self][0]
print(f'----------{self.my_name}.var adress:', id(a.var))
print(f'self.var in {self.my_name} instance:', id(self.var))
def change(self):
print(f'changing self.var value to 1 in {self.my_name} instance')
self.var = 1
b = a()
c = a()
b.foo()
c.foo()
c.change()
b.foo()
c.foo()
which outputs
----------b.var adress: 140434476089552
self.var in b instance: 140434476089552
----------c.var adress: 140434476089552
self.var in c instance: 140434476089552
changing self.var value to 1 in c instance
----------b.var adress: 140434476089552
self.var in b instance: 140434476089552
----------c.var adress: 140434476089552
self.var in c instance: 140434476089584
You can see that some write operation (self.var
= 1) in the c
context did create a new variable (an instance variable) for c. This is something you really have to be aware of, otherwise you won't work with class variables but only uncorrelated instance variables.
Instead, you should always use a.var
. This is also why method accessing class variables only should not have self
as a parameter, to avoid this confusion.
Upvotes: 0
Reputation: 13498
In python everything is an object, even classes themselves.
What:
class Coo(object):
num = 1
def foo(self):
print self.num
print Coo.num
does is that it creates a class object with the name Coo. It has attributes foo and num, with num being an int and foo being a function object.
coo = Coo()
Creates an instance object of Coo() that has the name coo. The instance coo contains no attributes:
print(coo.__dict__)
>>>{}
However, since you can do coo.foo()
, or coo.num
, coo clearly has attributes. The way coo gets the ability to use attributes from Coo
is how python's attribute lookup works.
For example when doing coo.num
, python attempts to look up num
inside of coo.__dict__
, but since it cannot find num, it moves into Coo.__dict__
, and finds entry {num:10}
The same thing happens when you try to call coo.foo()
, coo.__dict__
has no entry for foo
, but Coo.__dict__
does. coo.foo()
essentially becomes Coo.foo(coo)
, where the instance is passed in as self
. This phenomenon is what lets instances of classes use their class functions! They look it up inside of their class's __dict__
!
To use this to explain the anomaly in your question:
class Coo(object):
num = 1
def foo(self):
print self.num
print Coo.num
coo = Coo()
coo.foo()
coo
has no num
attribute, so num
is looked up inside Coo
and they both print 10.
class Coo(object):
num = 1
def foo(self):
self.num = 2
Coo.num = 3
print self.num
print Coo.num
coo = Coo()
coo.foo()
Here coo.__dict__
gains the entry {num:2}
when self.num=2
is declared. Then inside of Coo.__dict__
, num
is set to 3.
self.num
tries to look up num
inside of coo.__dict__
and finds it, printing 2
Coo.num
looks up num
inside of Coo.__dict__
and finds it, printing 3.
As for the best way to access class variables in a member function, you should just use Classname.varname
or self.__class__.varname
. This guarantees that you won't end up using the instance variable with the same name. However, it is good design to have class and instance variables have different names. This way no confusion should ever occur.
Upvotes: 2