Last active
November 16, 2021 16:06
-
-
Save goutomroy/f286c17d87d338e907dca9a715a3c002 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Meta(type): | |
""" | |
when | |
class MyClass(metaclass=Meta): | |
pass | |
is encountered type's __call__ method is called and from that Meta's __prepare__, __new__, __init__ | |
is called one by one. | |
""" | |
@classmethod | |
def __prepare__(mcl, name, bases): | |
""" | |
Called from type's __call__ method 1st | |
__prepare__ is the first method that is called during the creation of a class. | |
It is actually called before the class comes into existence. | |
It is supposed to return a dictionary like object that will be used as the namespace for the class. | |
Whatever method, attributes you define at the class level are stored in this dictionary. | |
This dictionary is then sent to __new__ and __init__ of meta class. | |
If you want to keep trace of order in which those methods, attributes, etc. were defined | |
you can return an OrderedDict. From Python 3.6 by default OrderedDict is returned | |
by the default implementation of __prepare__. | |
As mentioned before __prepare__ must be decorated with @classmethod decorator. | |
""" | |
empty_namespace_ordered_dict = super().__prepare__(name, bases) | |
print(f"id of namespace {id(empty_namespace_ordered_dict)}") | |
return empty_namespace_ordered_dict | |
# return dict(ami=1000) | |
@staticmethod | |
def __new__(mcs, name, bases, name_space: dict): | |
""" | |
Called from type's __call__ method 2nd | |
__new__ is called after __prepare__ and must return the class. | |
The class body is already executed by this point and the methods and other attributes are stored in | |
the dictionary (or dictionary like object) returned from the __prepare__ method. | |
You can do anything at this point with the class. | |
You can stop the creation of the class, remove methods and attributes from the class, add new methods | |
and attributes to the class, transform methods and attributes, etc. Your imagination is the limit here. | |
Remember __new__ from the metaclass is called only once. Because in the lifetime of your program | |
a class is created once. | |
""" | |
print(f"id of namespace {id(name_space)}") | |
return super().__new__(mcs, name, bases, name_space) | |
def __init__(cls, name, bases, name_space: dict): | |
""" | |
Called from type's __call__ method 3rd | |
__init__ is used for initialization purpose after the class has been created. | |
This method is not used much but you can use it if you need to carry out initialization task. | |
Always remember that __init__ of a metaclass is not an instance method but it is a class method and | |
it is called only once during the lifetime of the class. | |
This method must not return anything. | |
""" | |
print(f"id of namespace {id(name_space)}") | |
super().__init__(name, bases, name_space) | |
def __call__(cls, *args, **kwargs): | |
""" | |
Called when obj = MyClass() is encountered. | |
this method then calls MyClass's __new__ and __init__ respectively. | |
""" | |
# super().__call__(*args, **kwargs) | |
cls_obj = type.__call__(cls, *args, **kwargs) | |
return cls_obj | |
class MyClass(metaclass=Meta): | |
age = 10 | |
@staticmethod | |
def __new__(cls, *args, **kwargs): | |
# return super().__new__(cls, *args, **kwargs) | |
cls_obj = object.__new__(cls) | |
return cls_obj | |
def __init__(self, *args, **kwargs): | |
self.a = args[0] | |
self.b = args[1] | |
def multiply(self): | |
return self.a*self.b | |
@classmethod | |
def get_nothing(cls): | |
return 10 | |
obj = MyClass(10, 20) | |
print(obj.a) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment