I want to implement a singleton pattern in python, and I liked the pattern described in the http://www.python-course.eu/python3_metaclasses.php.
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class SingletonClass(metaclass=Singleton):
pass
class RegularClass():
pass
x = SingletonClass()
y = SingletonClass()
print(x == y)
x = RegularClass()
y = RegularClass()
print(x == y)
And the code works perfect. But, the __call__()
does not have the self
, and it also does not have @classmethod
or @staticmethod
declaration.
But, in the Python data model https://docs.python.org/3/reference/datamodel.html#object.__call__ the __call__()
method has a self in the arguments.
The code does not work if I pass self
, or declare as @staticmethod
or @classmethod
.
Can someone please explain the logic of the syntax behind the __call__()
method.
Naming the first argument of a method cls
or self
are just a convention. The __call__
method does have a self argument, only it is named cls
here. That's because for a metaclass, the method is bound to a class object, and the name reflects this.
The same convention is applied to @classmethod
methods; the first argument is a class, always, due to the nature of how a classmethod
object is bound, so it makes sense to name that first argument cls
.
But you are free to name that first argument anything else. It is not the name that makes a classmethod or a regular method or a method on a metatype work. All that using self
or cls
does is document what type of object this is, making it easier for other developers to mentally track what is going on.
So no, this is not an implicit class method. That first argument is not bound to the Singleton
metaclass object, it is bound to the class that was called. That makes sense, because that class object is an instance of the Singleton
metatype.
If you want to dive into how binding works (the process that causes that first argument to be passed in, whatever the name), you can read up on the Descriptor HOWTO. TLDR: functions, property
, classmethod
and staticmethod
objects are all descriptors, and whenever you access them as an attribute on a supporting object such as an instance or a class, they are bound, often causing a different object to be returned as a result, which when called passes in the bound object to the actual function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With