Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python dynamic decorators - why so many wraps?

So I'm still kind of new to Python decorators - I've used them before, but I've never made my own. I'm reading this tutorial (that particular paragraph) and I don't seem to understand why do we need three levels of functions? Why can't we do something like this:

def decorator(func, *args, **kwargs):
    return func(*args,**kwargs)

Thanks :)

like image 773
Justinas Avatar asked Apr 23 '11 15:04

Justinas


People also ask

Why do we need wrapper function in decorators?

Decorators allow us to wrap another function in order to extend the behavior of the wrapped function, without permanently modifying it. In Decorators, functions are taken as the argument into another function and then called inside the wrapper function.

Why you should wrap decorators in Python?

The Solution It's easy to understand it with the following example. As shown above, we just used the decorator wraps function to decorate the inner function by wrapping the say_hello function. It's just one line of code. Let's see how things are changed after this seemingly trivial change.

Is a Python decorator a wrapper?

In Python, a function decorator is effectively a function wrapper. A function decorator extends the functionality of a function by wrapping around it without modifying its original intended behavior.

What is the biggest advantage of the decorator in Python?

A decorator in Python is a function that takes another function as its argument, and returns yet another function . Decorators can be extremely useful as they allow the extension of an existing function, without any modification to the original function source code.


2 Answers

Well, what would happen if you called that decorator on a function?

@decorator
def foo(): pass

This code would immediately call foo, which we don't want. Decorators are called and their return value replaces the function. It's the same as saying

def foo(): pass
foo = decorator(foo)

So if we have a decorator that calls foo, we probably want to have a function that returns a function that calls foo -- that function that it returns will replace foo.

def decorator(f):
    def g(*args, **kwargs):
        return f(*args, **kwargs)
    return g

Now, if we want to pass options to the decorator, we can't exactly pass them ins ide-by-side with the function like in your example. There's no syntax for it. So we define a function that returns a parameterized decorator. The decorator it returns will be a closure.

def argument_decorator(will_I_call_f):
    def decorator(f):
        def g(*args, **kwargs):
            if will_I_call_f: return f(*args, **kwargs)
        return g
    return decorator

so we can do

decorator = argument_decorator(True)
@decorator
def foo(): pass

And Python offers the convenience syntax where you inline the function call:

@argument_decorator(True)
def foo(): pass

And all this is syntax sugar for the non-decorator syntax of

def foo(): pass
foo = argument_decorator(True)(foo)
like image 83
Devin Jeanpierre Avatar answered Sep 28 '22 06:09

Devin Jeanpierre


A decorator modifies a function by adding a wrapper to it. At the time you decorate the function, it isn't being called yet, so you don't have any arguments (or keyword arguments) to look at. All you can do for now is create a new function that will handle those arguments when it finally gets them.

like image 42
dfan Avatar answered Sep 28 '22 04:09

dfan