3
$\begingroup$

As a python interested guy i have seen the syntactic sugar of a decorator like this:

@decorator
def decorated_method(self):
    return "Hello"

So, it's a sort of higher order function in python, but my question is: Are there other patterns or ways to express the same idea of decorating beyond idiomatic python features, some more general concept under the computer science field?

Once upon a time in a nodejs course, the teacher mentioned "function decorations" but i didn't make sense of it clearly. Regards!

$\endgroup$
0

2 Answers 2

3
$\begingroup$

You already said it yourself, the Python decorator is just syntactic sugar for a high order function. When more than one decorator is applied, like in the example below, we could say it is also a kind of function composition

@f1(arg)
@f2
def func(): pass

is equivalent to

def func(): pass
func = f1(arg)(f2(func))

The Python PEP 318 where this feature was originally presented, mentions that:

There’s been a number of complaints about the choice of the name ‘decorator’ for this feature. The major one is that the name is not consistent with its use in the GoF book. The name ‘decorator’ probably owes more to its use in the compiler area – a syntax tree is walked and annotated. It’s quite possible that a better name may turn up.

A decorator is just a pattern, what matters usually is the intent. In object-oriented languages, you would implement it with a class decorator, and in functional languages, you'd resort to a function decorator.

$\endgroup$
2
  • 3
    $\begingroup$ Actually, if the decorator function invokes the decorated function then it is actually quite close to GoF usage, though there the emphasis is on objects and interfaces. $\endgroup$
    – Buffy
    Apr 27 at 22:19
  • $\begingroup$ @Buffy a function is just an object with a single method, the simplemost interface :-) $\endgroup$
    – Bergi
    Apr 28 at 4:43
1
$\begingroup$

All a decorator does is it allows you to build upon your function in some way i.e. it's a higher-order function. Ultimately, I prefer explaining them by "unrolling" the decorator.

# This:
@decorator
def foo(...):
    ...

# Becomes this:
def _foo(...):
    ...

def foo(...):
    ...

You can also express the same logic using OOP.

from abc import ABC, abstractmethod


class Decorator(ABC):

    def foo(...):
        ...

    @abstractmethod
    def _foo(...):
        ...


class Foo(Decorator):

    def _foo(...):
        ...

Inheritance makes sense in this case because you're essentially encapsulating logic that subclasses share. This example doesn't always work because you might want to use different method names for example, but concept is definitely there, and there's more sophisticated ways you can work around it all.

Ultimately however, decorators are higher-order functions. With that, I would suggest exercising them with some restraint, remembering that higher-order functions are for when you need the function itself and cannot separate the logic from it.

Common examples where using decorators might overcomplicate things in my experience include:

  • Pre-/post-processing e.g. input checking, which should just be a function call at the start/end(s) of your function.
# Turn this:
@type_check
def foo(...):
    ...

# Into this:
def foo(...):
    type_check(...)
    ...
  • Treating the function call like a block of code e.g. for timing, which can be done using with blocks instead.
# Turn this:
@timed
def foo(...):
    ...

# Into this:
def foo(...):
    with Timer():
        ...

Decorators are good for cases where the function being decorated is itself necessary, such as a function which needs to be saved to an API library that will call it for your API.

$\endgroup$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.