Decorator Base Class: Needs improvement.

Bengt Richter bokr at oz.net
Tue Apr 5 08:36:44 EDT 2005


On 5 Apr 2005 00:54:25 -0700, "Kay Schluehr" <kay.schluehr at gmx.net> wrote:

>Steve Holden wrote:
>
>> You have several times mentioned the possibility of a decorator
>taking
>> more than one argument, but in my understanding of decorators this
>just
>> wouldn't make sense. A decorator should (shouldn't it) take precisely
>
>> one argument (a function or a method) and return precisely one value
>(a
>> decorated function or method).
>
I agree from an English language point of view. I.e., a verber is
something that does the verbing, so a decorator ought to be the thing
that does the decorating, which is the function/callable(s) resulting
on stack from the evaluation of the @-line.

In the case of a single @deco name, the evaluation is trivial, and
the difference between the @-expression and the resulting callable
might be overlooked. Full-fledged general expressions after the '@'
are for some reason disallowed, but it is handy to allow attribute
access and calling in the syntax, so the relevant Grammar rules are:

>From the 2.4 Grammar, the key part seems to be

    decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
    decorators: decorator+
    funcdef: [decorators] 'def' NAME parameters ':' suite

and further on

    dotted_name: NAME ('.' NAME)*

So the Python Grammar's name for the @-expression is just plain "decorator"
which conflicts with my English-based reading of the word ;-/

So it appears the intent is to call the entire @-line the "decorator"
and I guess to have a name for what evaluating the @-line returns on the
stack, we could call it the "decorating callable" since it is what takes
the function parameter as its first parameter and decorates the function
and returns the decorated function.

But I don't like it, English-wise. I would rather call the @-line
the "decorator expression" and what it evaluates to the "decorator."

Can we change the grammar with s/decorator/decorator_expr/ ?

>Yes. I think this sould be fixed into the minds of the people exacly
>this way You state it:
I think we may be agreeing in principle but with different words ;-)
>
>When writing
>
>@decorator(x,y)
>def f():
>    ....
>
>not the so called "decorator" function but decorator(x,y) is the
                                           ^--(the result of evaluating)
>decorating function and decorator(x,y) is nothing but a callable object
                        ^--(the result of evaluating)
>that takes f as parameter. A little correcture of Your statement: it is
>NOT nessacary that a function or method will be returned from a
>decorator.

Yes, in fact it could even perversely be colluding with a known succeeding
decorator callable to pass info strangely, doing strange things, e.g.,

 >>> trick = ['spam', 'eggs']
 >>> def choose_name(tup):
 ...     nx, f = tup
 ...     f.func_name = trick[nx]
 ...     return f
 ...
 >>> def namedeco(nx=1):
 ...     return lambda f, nx=nx:(nx, f)
 ...
 >>> @choose_name
 ... @namedeco()
 ... def foo(): pass
 ...
 >>> foo
 <function eggs at 0x02EE8E64>
 >>> @choose_name
 ... @namedeco(0)
 ... def foo(): pass
 ...
 >>> foo
 <function spam at 0x02EE8DF4>

I.e., namedeco evaluates to the lambda as decorator function,
and that passes a perverse (nx, f) tuple on to choose_name, instead
of a normal f.

>
>def decorator(x,y):
>    def inner(func):
>        return x+y
>    return inner
>
>@decorator(1,2)
>def f():pass
>
>>>> f
>3
>
>This is perfectly valid allthough not very usefull ;)
>

Perhaps even less useful, the final decorator can return something
arbitrary, as only the name in the def matters at that point in
the execution (as a binding target name), so:

 >>> def dumbdeco(f): return 'something dumb'
 ...
 >>> @dumbdeco
 ... def foo(): pass
 ...
 >>> foo
 'something dumb'

Hm, maybe some use ...

 >>> def keydeco(name):
 ...     return lambda f: (name, f)
 ...
 >>> @keydeco('pooh_foo')
 ... def foo(): pass
 ...
 >>> @keydeco('tigger_bar')
 ... def bar(): pass
 ...
 >>> dict([foo, bar])
 {'pooh_foo': <function foo at 0x02EE8DBC>, 'tigger_bar': <function bar at 0x02EE8DF4>}

... nah ;-)


Anyway, I think a different name for what comes after the "@" and the
callable that that (very limited) expression is supposed to return
would clarify things. My conceptual model is

    @decorator_expression  # => decorator
    def decorating_target(...):
        ...

Regards,
Bengt Richter



More information about the Python-list mailing list