Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class decorators vs function decorators [duplicate]

In python there are two ways to declare decorators:

Class based

class mydecorator(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, *k, **kw):
        # before f actions
        self.f(*k, **kw)
        # after f actions

Function based

def mydecorator(f):
    def decorator(*k, **kw):
        # before f actions
        f(*k, **kw)
        # after f actions

    return decorator          

Is there any difference between these declarations? In which cases each of them should be used?

like image 951
Yossi Avatar asked Jul 13 '11 08:07

Yossi


People also ask

Can a function have 2 decorators?

So, here in this post, we are going to learn about Decorator Chaining. Chaining decorators means applying more than one decorator inside a function. Python allows us to implement more than one decorator to a function.

What are the function and class decorators used for?

A decorator in Python is any callable Python object that is used to modify a function or a class. A reference to a function "func" or a class "C" is passed to a decorator and the decorator returns a modified function or class.

Why decorators in Python are pure genius?

Decorators are a prime-time example of a perfectly implemented feature. It does take a while to wrap your head around, but it's worth it. As you start using them, you'll notice how they don't overcomplicate things and make your code neat and snazzy.

Can decorators be async?

A decorator can be used to simply and cleanly implement retry functionality for asynchronous functions.


4 Answers

If you want to keep state in the decorator you should use a class.

For example, this does not work

def mydecorator(f):     x = 0      def decorator():         x += 1 # x is a nonlocal name and cant be modified         return f(x)     return decorator  

There are many workarounds for this but the simplest way is to use a class

class mydecorator(object):     def __init__(self, f):         self.f = f         self.x = 0      def __call__(self, *k, **kw):         self.x += 1         return f(self.x) 
like image 52
Jochen Ritzel Avatar answered Sep 21 '22 11:09

Jochen Ritzel


When you're creating a callable returning another callable, the function approach is easier and cheaper. There are two main differences:

  1. The function approach works automatically with methods, while if you're using your class approach, you'd have to read on descriptors and define a __get__ method.
  2. The class approach makes keeping state easier. You could use a closure, especially in Python 3, but a class approach is generally preferred for keeping state.

Additionally, the function approach allows you to return the original function, after modifying it or storing it.

However, a decorator can return something other than a callable or something more than a callable. With a class, you can:

  1. Add methods and properties to the decorated callable object, or implement operations on them (uh-oh).
  2. Create descriptors that act in a special way when placed in classes (e.g. classmethod, property)
  3. Use inheritance to implement similar but different decorators.

If you have any doubt, ask yourself: Do you want your decorator to return a function that acts exactly like a function should? Use a function returning a function. Do you want your decorator to return a custom object that does something more or something different to what a function does? Create a class and use it as a decorator.

like image 34
Rosh Oxymoron Avatar answered Sep 19 '22 11:09

Rosh Oxymoron


In fact there are no 'two ways'. There is only one way (define a callable object) or as many ways as there are in python to make a callable object (it could be a method of other object, a result of lambda expression, a 'partial' object, anything that is callable).

Function definition is the easiest way to make a callable object and, as the simplest one, is probably the best in most cases. Using a class gives you more possibilities to cleanly code more complicated cases (even in the simplest cases it looks quite elegant), but it is not that obvious what it does.

like image 41
Jacek Konieczny Avatar answered Sep 21 '22 11:09

Jacek Konieczny


No, there are (more than) two ways to make callable objects. One is to def a function, which is obviously callable. Another is to define a __call__ method in a class, which will make instances of it callable. And classes themselves are callable objects.

A decorator is nothing more than a callable object, which is intended to accept a function as its sole argument and return something callable. The following syntax:

@decorate
def some_function(...):
    ...

Is just a slightly nicer way of writing:

def some_function(...):
    ...

some_function = decorate(some_function)

The class-based example you give isn't a function that takes a function and return a function, which is the bog-standard vanilla decorator, it's a class that is initialised with a function whose instances are callable. To me, this is a little weird if you're not actually using it as a class (Does it have other methods? Does its state change? Do you make several instances of it that have common behaviour encapsulated by the class?). But normal use of your decorated function will not tell the difference (unless it's a particularly invasive decorator), so do whatever feels more natural to you.

like image 23
Ben Avatar answered Sep 19 '22 11:09

Ben