Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How decorators work with classes in python

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?

like image 637
A. Churshin Avatar asked Feb 10 '16 00:02

A. Churshin


People also ask

Can Python decorators be used on classes?

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.

How does a decorator work in Python?

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.

Which decorator is used to identify a class?

The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition.

What decorator is used to make a class a service?

The @Injectable() decorator defines a class as a service in Angular and allows Angular to inject it into a component as a dependency.


3 Answers

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

like image 90
John La Rooy Avatar answered Nov 11 '22 05:11

John La Rooy


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.

like image 39
kindall Avatar answered Nov 11 '22 03:11

kindall


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>
like image 27
Aviah Laor Avatar answered Nov 11 '22 03:11

Aviah Laor