Last active
December 23, 2022 05:46
-
-
Save ychennay/e3ed251da577306831f677af24677a6e to your computer and use it in GitHub Desktop.
Example of metaclass
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 ModelBase(type): | |
a_variable_from_metaclass = 3 | |
_instances = dict() # keep track of the instances created per class | |
@classmethod | |
def __prepare__(metacls, name, bases, **kwargs): # called first | |
print(f"\nInside MetaClass __prepare__, name: {name}, bases: {bases}, metacls: {metacls}, kwargs: {kwargs}") | |
namespace = super().__prepare__(name, bases, **kwargs) | |
print(f"Returned namespace: {namespace}") | |
return namespace | |
def __new__(cls, name, bases, namespaces, **kwargs): # called second, and constructs the new class | |
print( | |
f"\nInside MetaClass __new__(), name: {name}, bases: {bases}, cls: {cls}, kwargs: {kwargs}, namespaces: {namespaces}") | |
namespaces["some_variable"] = 5 # setting a variable | |
for attribute_name, attribute_value in namespaces.items(): | |
# iterate through the provided namespace, and change all the integer values to instances of ModelField | |
if isinstance(attribute_value, int): | |
namespaces[attribute_name] = ModelField(attribute_value) | |
new_class = super().__new__(cls, name, bases, namespaces, **kwargs) | |
print(f"Output of __new__: {new_class}") | |
return new_class | |
def __init__(cls, name, bases, namespace, **kwargs): # called third, initializes the newly created class with values | |
# By the time __init__ is called, the new class exists | |
print("\nInside __init__ of MetaClass") | |
print(f"cls: {cls}, name: {name}, bases: {bases}, namespace: {namespace}, kwargs: {kwargs}") | |
ModelBase.__setattr__(cls, "some_variable", 4) | |
cls.variable_added_during_init = "boom" | |
namespace["some_variable"] = 4 # attempt to change the attribute some_variable's value to 4 | |
super().__init__(name, bases, namespace) | |
def __call__(cls, *args, **kwargs): # called when new instances are created, delegates initialization to __init__ | |
print(f"Inside __call__ of MetaClass.") | |
print(f"cls: {cls}, args: {args}, kwargs: {kwargs}") | |
if cls not in cls._instances: # https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python | |
cls._instances[cls] = super().__call__(*args, **kwargs) | |
print(f"Output of __call__: {cls._instances[cls]}") | |
return cls._instances[cls] |
Hi @ychennay. Thank you for creating this. Isn't line 34-36 unnecessary?
Yes you are right - silly typo. I've removed it.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @ychennay. Thank you for creating this. Isn't line 34-36 unnecessary?