Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I access class variables using self?

I have a class Foo with a class variable remote. Can I access the class variable remote using self.remote?

class Foo:
   remote = False

   def __init__(self):
       self.remote = True 

   @classmethod
   def print_remote(cls):
       print(cls.remote) #prints False but why? 
like image 313
2ank3th Avatar asked Jun 09 '17 14:06

2ank3th


2 Answers

Assigning remote to self in __init__ means that instance.remote is found first when you access it through self (granted no descriptors are around). To get both options, access either from self or from type(self), that is, either from the instance or the class:

def print_remote(self):
    print(type(self).remote) # class remote
    print(self.remote)       # instance remote

type(self).remote is essentially equivalent to self.__class__.remote but, in general, you should avoid grabbing dunder names (__*__) when there's a built in that does it for you (type in this case)

These live in different dictionaries and are different variables. self.remote lives in the instance dict while class.remote in the class dict.

>>> Foo().__dict__['remote']
True
>>> Foo.__dict__['remote']
False

When you access through cls with a classmethod (or type(self) in a normal method) you'll get the class one, when you access through self you get the instance one.

like image 183
Dimitris Fasarakis Hilliard Avatar answered Oct 01 '22 12:10

Dimitris Fasarakis Hilliard


In [1]: class Foo:
   ...:     x = 0
   ...:

In [2]: f = Foo()

In [4]: f.__dict__ # empty
Out[4]: {}

In [5]: Foo.__dict__ # have the variable x = 0
Out[5]:
mappingproxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Foo' objects>,
              'x': 0})

When you try access a variable in a object, Python will look first in the object, if it is not there then it looks in class dict.

In [6]: Foo.x = 10 # changing class variable

In [7]: f.__dict__ # still empty.
Out[7]: {}

In [8]: f.x # gives you Foo's x as object doesn't have that item.
Out[8]: 10

In [9]: f.x = 20 # this line creates a new variable in x. Now both class and object has their own variable x

In [10]: f.__dict__ # f has its own x.
Out[10]: {'x': 20}

In [11]: Foo.__dict__ # Foo has its own x.
Out[11]:
mappingproxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,
              ...
              'x': 10})
In [12]: f.x # always you will get the value from f.__dict__
Out[12]: 20
In [16]: f.x = 50 # changing the value of object's variable

In [17]: Foo.x # above statement didn't affect class's variable.
Out[17]: 10

In [13]: del f.x # delete object's x

In [14]: f.x # now f doesn't have x, you get the value from class Foo.
Out[14]: 10
like image 38
thavan Avatar answered Oct 01 '22 12:10

thavan