On 06.08.20 04:58, Guido van Rossum wrote:

On Wed, Aug 5, 2020 at 6:42 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Wed, Aug 05, 2020 at 06:15:22PM -0700, Guido van Rossum wrote:
> On Wed, Aug 5, 2020 at 5:55 PM Steven D'Aprano <steve@pearwood.info> wrote:

> > That require two different rules for decorators:
> >
> > @decorator over a `class name` or `def name` statement:
> >
> > - execute the statement
> > - bind `name = decorator(name)`
> >
> But that's not what's done. (Proof: if the decorator raises, the name
> remains unbound.)

You are technically correct, which is the best kind of correct.

The documentation uses very close to the same wording as me


but does make the point that "except that the original function is not
temporarily bound to the name func". Since I wasn't writing a reference
manual, I didn't think this level of pedantry was needed :-)

The bottom line is that the function or class statement has to be
executed *in some sense* in order to create the function or class
object, that object has to be passed to the decorator, and finally the
object returned by the decorator has to be bound to the original name.

But that's the same as it would be for a decorated assignment, right? In
x = func(arg)
This executes `func(arg)` to create the value of the expression, then passes it to the decorator, and finally the decorator's result is bound to the name `x`.

In both cases there's something that gets executed to create something (in one case, a function or class object, in another case, some other object), and then gets bound to a name; in both cases a decorator, if present, is inserted to transform the value just before it is bound.

A much better argument against decorating assignments is that you can already write it just fine as
x = deco(func(arg))
and the decorated version is in no way more readable, nor does it provide more power or expressivity.

It seems that the OP has many such transformations and wants to use decorators to define a pipeline of transformations:

    something = initial_value

instead of

    something = foo(bar(baz(initial_value)))

which becomes unreadable for longer function names / more functions in general.

However one can always define such pipelines in advance and then apply them when needed:

    pipeline = Pipeline(foo, bar, baz)
    something = pipeline(initial_value)

For single usage one can even define such a pipeline via decorators by using a function to provide the initial value:

    def something():
        return initial_value

where `pipeline` simply returns the return value of the function it's applied to.