[Python-Dev] Re: PEP 318: Decorators last before colon

Gareth McCaughan gmccaughan at synaptics-uk.com
Mon Apr 5 12:10:42 EDT 2004


On Monday 2004-04-05 at 15:42, Andrew Koenig wrote:

> Here's what I don't understand.
> 
> I imagine that most of the time, when someone decorates a function with lots
> of attributes, there is at least the possibility of decorating more than one
> function with the same set of attributes.
> 
> In such a case, doesn't it make sense to bind a variable to the attributes
> and use it instead?
> 
> 	Attributes = [staticmethod, classmethod,
> otherattributes(debug=True)]
> 
> 	def foo(bar, baz) Attributes:
> 		pass
> 
> And doesn't this idea answer the objection that too many attributes after
> the parameter list make the definition hard to read?

It certainly answers that objection, but it has (so it seems to me)
other problems of its own; see below.

> For that matter, why do we need the brackets if there is only one attribute:
> 
> 	def foo(bar, baz) staticmethod:
> 		pass
> 
> I am suggesting that what comes between the ) and the : should be an
> expression, which must evaluate to either a callable or a sequence of
> callables.  For that matter, why not allow a tuple expression without
> parentheses:
> 
> 	def foo(bar, baz) staticmethod, classmethod:
> 		pass
> 
> Whatever sequence you put there, I think the semantics are clear:  Before
> binding a name to the function, pass it to the callable or in turn to each
> element of the sequence.

The brackets serve multiple purposes.

  - They make the decoration *look*, even to novice eyes, like
    an annotation: "by the way, please make this a static method".

  - They may possibly help the parser a little. (I'm showing
    my ignorance here; I haven't really looked at the Python
    parser at all.)

  - They give a hint to the user that what goes in between them
    may be a sequence, not just a single item.

  - They ensure that all decorated definitions have a somewhat
    consistent appearance.

Omitting them breaks all these things, especially if an arbitrary
expression is allowed there. And do we really want to allow

    def foo(bar, baz) quux(wibble, spong):
        pass

That's not a hideous pathological case; it's what a simple
decoration using a parameterized decorator will look like
without the brackets. With good decorator names I suppose
it becomes somewhat comprehensible:

    def foo(bar, baz) memoized(25):
        pass

but surely it's still much clearer when written as

    def foo(bar, baz) [memoized(25)]:
        pass

or even (though I'm a little tempted to agree with whoever it was
that was wondering whether Guido has been abducted by aliens)

    [memoized(25)]
    def foo(bar, baz):
        pass

We can keep the syntactic distinctiveness while still allowing
multiple decorators to be combined, by having a function
(a builtin, perhaps, but it's not hard to write) that
composes decorators:

    def compose(*decorators):
        def _(f):
            for d in decorators: f = d(f)
            return f

    our_attributes = compose(staticmethod, classmethod,
      otherattributes(debug=True))

    def foo(bar, baz) [our_attributes]:
        pass

-- 
Gareth McCaughan





More information about the Python-Dev mailing list