Skip to content

Instantly share code, notes, and snippets.

@goutomroy
Last active November 16, 2021 16:06
Show Gist options
  • Save goutomroy/f286c17d87d338e907dca9a715a3c002 to your computer and use it in GitHub Desktop.
Save goutomroy/f286c17d87d338e907dca9a715a3c002 to your computer and use it in GitHub Desktop.
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