The metaclasses are responsible for the instantiation process.
- By default, the metaclass of any Python class is
type
. same asclass MyClass(metaclass=type)
Whenever, you do my_class = MyClass()
:
MyClass.__call__()
method is being called. This happens because in Python, calling an instance as a function, calls the__call__()
method of the class.- Inside the
MyClass.__call__()
method of the class:- The
MyClass.__new__()
method is being called to create a new instance. MyClass.__new__()
is a stub method calling the parent's.__new__()
, in this casetype.__new__()
).- It looks like this:
def __new__(cls, *args, **kwargs): return super().__new__(cls, *args, **kwargs)
- The
- Then the
type.__new__()
method, which is the constructor, constructs the instance and returns it to interpreter. - Python VM creates a
__dict__
attribute if__slots__
is not present. MyClass.__init__()
method receives the instace and initializes it.
Using them you canInside of them can be defined the custom class creation
- In Python metaclsses:
- Control the class creation process. Using a metaclass you can redefine the
__new__
method -
def new(cls, name, bases, dct): print("Creating class:", name) return super().new(cls, name, bases, dct) class MyClass(metaclass=MyMeta):
pass - Control the class creation process. Using a metaclass you can redefine the
- ```python
class MyMeta(type):
def __new__(cls, name, bases, dct): # Add a custom method to the class setattr(cls, 'custom_method', lambda self: print("Custom method called")) class MyClass(metaclass=MyMeta):
pass
Сниглтон: Это для того чтобы подставить существующий экземпляр под __init__
__init__
в любом случае будет выполняться
No need for parent-singleton to provide *args, **kwargs
to __init__
There're two ways of doing Singleton
- Through metaclass: for one or more classes
- In-place implementation : for one class
For a metaclass singleton instances = set()
is recommended.
- As the metaclass will be reused in multiple classes the set
instances
will hold all of them inside.
A bit about metaclasses:
class Singleton(type):
instances = set() def __new__(cls, *args, **kwargs):
instance = super().__new__(cls, args, kwargs) if instance not in cls.instances:
cls.instances.add(instance) return instance
class MyClass(metaclass=Singleton):
...
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
class MyClass(Singleton):
...
class ParentClass:
pass
class ChildClass(ParentClass, metaclass=Singleton):
pass ...
class SingletonClass:
_instance = None
_initialized = False
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, value):
if not self._initialized:
self.value = value
self._initialized = True
- Using
__call__
magic method__call__
method is being executed upon class instantiation.m = MyClass()
here__call__
is being executed- So, the sequence is:
__call__
=>__new__
=>__init__
ConsequentlySingleton
can be created using__call__
as well:
- So, the sequence is:
-
_instances = set() def call(cls, *args, **kwargs):
instance = super().call(*args, **kwargs) if cls not in cls._instances:
cls._instances.add(instance) return instance