A class is a structure which abstract the state and behavior that manipulates the state.
In python, a class can be defined as follows.
class Voter:
def __init__(self):
self.age = 21
def eligible_to_vote(self):
return self.age >= 18Instance
A class instance is the runtime structure that gets created having unique state.
>>> voter = Voter()It can be created by calling the class as function. We can then access the instance attributes and methods.
>>> voter.eligible_to_vote()
>>> voter.ageInstance’s data attributes are stored in __dict__ property on the instance as can be seen below.
>>> voter.__dict__
{'age': 21}Class attributes
class Voter:
eligible_age = 18eligible_age attribute belongs to the class. Similar to instance, it also gets stored into class’s __dict__.
>>> Voter.__dict__
mappingproxy({'__module__': '__main__',
'eligible_age': 18,
'__dict__': <attribute '__dict__' of 'Voter' objects>,
'__weakref__': <attribute '__weakref__' of 'Voter' objects>,
'__doc__': None})Note
Functions are also gets stored in
__dict__in the case of class.
Attribute Lookup
Attributes lookup on instance is starts from __dict__ of instance, and then goes to class’s __dict__ if not found.
For example,
class Voter:
eligible_age = 18
def __init__(self):
self.age = 21
def eligible_to_vote(self):
return self.age >= 18>>> voter = Voter()
>>> voter.age
21When we say voter.age, it looks for age attribute in __dict__ of the instance.
>>> voter.eligible_age
18eligible_age is not present in instance’s __dict__, however it is found in class’s __dict__.
How does method work?
Function attributes of the classes, eligible_to_vote in this case, are just usual python function but in class namespace. We can check that as follows,
>>> Voter.eligible_to_vote
<function __main__.Voter.eligible_to_vote(self)>When we refer a non data attribute on the instance, the instance’s class is search for that attribute and if the attribute is a function object, the instance and the function is packed into a method.
So, the following example shows a bounded method on the voter instance.
>>> voter.eligible_to_vote
<bound method Voter.eligible_to_vote of <__main__.Voter object at 0x10ce336d0>>When we invoke the method with a given arguments list, a new arguments list is prepared with the instance as the first argument and sent to the method. That is why there is always a self as a first argument to the function.
Note
Using
selfis not a rule rather a convention in python. We can any name for it. However, it is recommended to follow the convention. There might be libraries which might be following this convention that you may be using.
@classmethod and @staticmethod
@classmethod and @staticmethod are descriptors which changes the attributes lookup for function.
@classmethod or just classmethod creates the function as class method. This means that it bounds the function with the class.
class Voter:
eligible_age = 18
@classmethod
def get_eligible_age(cls):
return cls.eligible_ageOr
def get_eligible_age(cls):
return cls.eligible_age
class Voter:
eligible_age = 18
get_eligible_age = classmethod(get_eligible_age)Similarly, @staticmethod creates function which is not bound to anything. It is just there in class for namespace.
class Voter:
@staticmethod
def get_eligible_age():
return 18Python knows that these function attributes are descriptors and it calls them in appropriate way.