1

It was bothering me for a long time. Why are decorator functions designed like that, they seem to me over complicated. Let's take for example something like this:

def dec(f):
    def wrapper(a, b):
        print('Hello')
        f(a, b)
        print('Bey')
    return wrapper

@dec
def func(a, b):
    print(a)
    print(b)

Why do an additional function in the decorator, to wrap the functionality in the dec function? I understand that the engine works like that, but why not make something simpler. Like this:

def dec(f, a, b):
    print('Hello')
    f(a, b)
    print('Bey')

@dec
def func(a, b):
    print(a)
    print(b)

Change a little bit the engine and make the @ operator pass the name of the function and the parameters as parameters to the dec function. What are the benefits to use the first structure as we can easily decorate the second the same way?

If you can give me an example where the second example could not be possible to solve a problem - please share your knowledge.

The ticket was closed because some of the users have required more clarity to the problem, to help me solve the problem. I need to say that there is no problem to be solved. I have placed this question to collect information on why decorators are designed in the way they are. This will help me to understand what additional problems I can solve in the future with the tricks I can do with the existing model of the decorators.

Sorry if the question sounded unclear to you in any way. I thought it would be interesting to hear what experienced python users could say about this and share their knowledge with anyone who doesn't understand the purpose of a wrapper in a decorative function.

Igor Dragushhak
  • 567
  • 1
  • 3
  • 14
  • 7
    Because the decorator syntax, `@` came *after* the idea of a decorator, which is a callable that returns a callable, that modifes some action of that callable passed in. `@dec def func` is just syntactic sugar for `def func()...; func = deco(func)`. Basically, you are asking "why is a decorator a decorator" – juanpa.arrivillaga Jun 23 '20 at 21:57
  • 1
    Anyway, you don't *have* to define a function. A decorator could do somthing else, and then simply `return f` Your idea would *force* it to act as a wrapper. – juanpa.arrivillaga Jun 23 '20 at 21:59
  • I don't have an answer, but just thinking, how would you use [`functools.wraps`](https://docs.python.org/3/library/functools.html#functools.wraps) with your syntax? – wjandrea Jun 23 '20 at 22:00
  • @juanpa.arrivillaga, but they could change the @ operator functionality, so are they just not doing that because everyone is use to this model? – Igor Dragushhak Jun 23 '20 at 22:00
  • 2
    @IgorDragushhak again, **because a decorator is simply a callable that returns a callable**. It doesn't *have* to define a wrapper. Your idea would be forcing it to. Also, again, the syntactic sugar came *after* the idea. You would have to re-write all the decorators that existed before 2003 to work like what you provided. Also, how would it work with non-function decorators, (class-based decorators)? – juanpa.arrivillaga Jun 23 '20 at 22:02
  • The function passed in the decorator syntax doesn't even *need* to return a callable, it can return anything. – Iain Shelvington Jun 23 '20 at 22:03
  • Also, you couldn't use your function normally, i.e. `func = deco(func)`, which in many cases is useful (e.g. dynamically decorating functions). – juanpa.arrivillaga Jun 23 '20 at 22:06
  • @IgorDragushhak The decorator returning a function has the advantage that you call it once with the function you want to wrap, and then you have a new callable which takes the same arguments as the original function but includes the bolt-on functionality. Whereas with your wrapper that takes as its arguments the wrapped function together with the arguments of that function, you now have to change your calling code to use this modified argument list. – alani Jun 23 '20 at 22:07
  • @Igor Dragushhak The rationale for the decorator syntax is [outlined in PEP 318](https://www.python.org/dev/peps/pep-0318/#current-syntax). – ekhumoro Jun 23 '20 at 22:20
  • @IgorDragushhak SO isn't the place for extended discussions. I suggest you read the PEP I linked to in my previous comment, then if you still have questions, [the python mailing list](https://mail.python.org/mailman/listinfo/python-list) would probably be a better place to ask them. – ekhumoro Jun 23 '20 at 22:27
  • 1
    There would have been signficant advantages to making `@dec(a, b)` call `dec(f, a, b)` instead of `dec(a, b)(f)`. For example, unifying `@dec` and `@dec()`, making it much easier to add optional arguments to a decorator that didn't take them before, and avoiding cases where the function gets mixed up with another argument. I have long wished that Python's decorator syntax had been designed that way. – user2357112 Jun 23 '20 at 22:29
  • @user2357112supportsMonica thank you for supporting me and getting involved in the conversation. It is nice to know that someone is really interested in the topic. – Igor Dragushhak Jun 23 '20 at 22:32
  • @IgorDragushhak: I actually misread your proposal initially - what I was thinking of is something different. I've edited my comment. – user2357112 Jun 23 '20 at 22:33
  • 1
    This question's proposed semantics have significant limitations. Particularly, decorators with parameters (like `@dec(a, b)`) are no longer supported, and decorators like `@property` that create non-function objects are no longer supported. – user2357112 Jun 23 '20 at 22:36
  • @user2357112supportsMonica nevertheless, it is still good that you are getting involved. The more people are discussing the problem, the more information I can get out of this. It's a pity that the question is closed. – Igor Dragushhak Jun 23 '20 at 22:37
  • @user2357112supportsMonica couldn't the property decorator be somehow changed to fit the model I have mentioned? – Igor Dragushhak Jun 23 '20 at 22:39
  • @IgorDragushhak: With your proposed design, applying `@dec` to `func` replaces `func` with a callable that delegates to `dec`. There is no way to replace `func` with something else, and the decorator doesn't actually get to do anything until `func` is called. – user2357112 Jun 24 '20 at 00:23

1 Answers1

0

A decorator should return a function not the result of a function. In your first example, it returns a function ... In the second, it returns whatever your function f returns. - julien

  1. Why second sample does not works?

    Because you are calling the function on the return, you are not returning a function.

  2. Why args,kwargs are "magically" appears if we are using a nested function?

    They don't appear magically, we are declaring them, as in:

    def deco(*args, **kwargs):
    

    These are generic, and will match any function signature (argument list). You don't have to call them args and kwargs, that's just a convention, you could call them sharon and tracy.

  3. What can i do, to make 2nd sample work? Except nesting another function, ofcourse.

    Well you don't say what you expect the 2nd sample to do. But I guess to turn it into a decorator then:

    def func(f):
        return f
    

    But that's not doing a lot!

By the way, it is usually a bad idea to override an existing Python builtin (sum) - you have to have a very good reason for that. - cdarke

Source

7u5h4r
  • 459
  • 3
  • 10