I've tried to understand how a certain singleton decorator implementation for a class works, but I only got confused.
Here's the code:
def singleton(cls):
instance = None
@functools.wraps(cls)
def inner(*args, **kwargs):
nonlocal instance
if instance is None:
instance = cls(*args, **kwargs)
return instance
return inner
@deco
is a syntatic sugar for cls = deco(cls)
, so in this code, when we define our cls
class and wrap it with this singleton
decorator, cls
won't be a class anymore, but a function. Python dynamically searches for what objects are variables are linked to, so then later we try to create an instance of our class, and this line of code runs instance = cls(*args, **kwargs)
, won't we go into an infinite recursion? cls
is not a class at this moment, it's a function, so it should call itself, going into recursion.
But it works fine. A singletone is created and no recursions happen. How does it work?
In Python, decorators can either be functions or classes. In both cases, decorating adds functionality to existing functions. When we decorate a function with a class, that function becomes an instance of the class.
Decorators provide a simple syntax for calling higher-order functions. By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.
The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition.
The @Injectable() decorator defines a class as a service in Angular and allows Angular to inject it into a component as a dependency.
The function inner
is closed over the local variable cls
.
cls
is a reference to the class. It is never rebound to anything else.
The decorator returns a function that returns an instance, but this does not affect what the inner cls
variable refers to
cls
is a reference to the original class passed in to the decorator. It retains the value it had when the decorator was called. Its value is "trapped" in the function returned by the decorator; for obscure reasons, this is called a closure. Most languages in which functions are first-class objects have this capability.
def singleton(cls):
instance = None
@functools.wraps(cls)
def inner(*args, **kwargs):
nonlocal instance
if instance is None:
instance = cls(*args, **kwargs)
print cls
return instance
return inner
@singleton
class TryMe(object):
pass
m = TryMe()
print m
You will get:
<class '__main__.TryMe'>
<__main__.TryMe object at 0x10231c9d0>
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