Re: [Python-Dev] [Python-checkins] peps: Add PEP 422: Dynamic Class Decorators

25 views
Skip to first unread message

Terry Reedy

unread,
Jun 5, 2012, 12:42:38 PM6/5/12
to pytho...@python.org
On 6/5/2012 8:09 AM, nick.coghlan wrote:

> Add PEP 422: Dynamic Class Decorators

> +Iterating over decorator entries in reverse order
> +-------------------------------------------------
> +
> +This order was chosen to match the layout of lexical decorators when
> +converted to ordinary function calls. Just as the following are equivalent::
> +
> + @deco2
> + @deco1
> + class C:
> + pass
> +
> + class C:
> + pass
> + C = deco2(deco1(C))
> +
> +So too will the following be roughly equivalent (aside from inheritance)::
> +
> + class C:
> + __decorators__ = [deco2, deco1]

I think you should just store the decorators in the correct order of use
+ __decorators__ = [deco1, deco2]
and avoid the nonsense (time-waste) of making an indirect copy via
list_iterator and reversing it each time the attribute is used.

If the list is constructed in reversed order, immediately reverse it.

> +
> + class C:
> + pass
> + C = deco2(deco1(C))

Terry Jan Reedy
_______________________________________________
Python-Dev mailing list
Pytho...@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/dev-python%2Bgarchive-30976%40googlegroups.com

PJ Eby

unread,
Jun 5, 2012, 2:26:10 PM6/5/12
to Terry Reedy, pytho...@python.org

It's for symmetry and straightforward translation with stacked decorators, i.e. between:

@deco1
@deco2
[declaration]

and __decorators__ = [deco1, deco2]

Doing it the other way now means a different order for people to remember; there should be One Obvious Order for decorators, and the one we have now is it.

Jan Kaliszewski

unread,
Jun 5, 2012, 5:30:38 PM6/5/12
to pytho...@python.org
Terry Reedy dixit (2012-06-05, 12:42):

> On 6/5/2012 8:09 AM, nick.coghlan wrote:
>
> > Add PEP 422: Dynamic Class Decorators
[snip]
> >+So too will the following be roughly equivalent (aside from inheritance)::
> >+
> >+ class C:
> >+ __decorators__ = [deco2, deco1]
>
> I think you should just store the decorators in the correct order of use
> + __decorators__ = [deco1, deco2]
> and avoid the nonsense (time-waste) of making an indirect copy via
> list_iterator and reversing it each time the attribute is used.

+1. For @-syntax the inverted order seems to be somehow natural. But I
feel the list order should not mimic that...

***

Another idea: what about...

@@dynamic_deco2
@@dynamic_deco1
class C:
pass

...being an equivalent of:

class C:
__decorators__ = [dynamic_deco1, dynamic_deco2]

...as well as of:

@@dynamic_deco2
class C:
__decorators__ = [dynamic_deco1]

?

Cheers.
*j

Terry Reedy

unread,
Jun 5, 2012, 5:31:15 PM6/5/12
to pytho...@python.org
You and I have different ideas of 'obvious' in this context. But since
you will use this and and me probably not, let your idea rule.

--

PJ Eby

unread,
Jun 5, 2012, 7:06:38 PM6/5/12
to Terry Reedy, pytho...@python.org
On Tue, Jun 5, 2012 at 5:31 PM, Terry Reedy <tjr...@udel.edu> wrote:
On 6/5/2012 2:26 PM, PJ Eby wrote:
On Tue, Jun 5, 2012 at 12:42 PM, Terry Reedy <tjr...@udel.edu
<mailto:tjr...@udel.edu>> wrote:

   I think you should just store the decorators in the correct order of use
   +        __decorators__ = [deco1, deco2]
   and avoid the nonsense (time-waste) of making an indirect copy via
   list_iterator and reversing it each time the attribute is used.


It's for symmetry and straightforward translation with stacked
decorators, i.e. between:

@deco1
@deco2
[declaration]

and __decorators__ = [deco1, deco2]

Doing it the other way now means a different order for people to
remember; there should be One Obvious Order for decorators, and the one
we have now is it.

You and I have different ideas of 'obvious' in this context.

To be clearer: I've written other APIs which take multiple decorators, or things like decorators that just happen to be a pipeline of functions to be applied, and every time the question of what order to put the API in, I always put them in this order because then in order to remember what the order was, I just have to think of decorators.  This is easier than trying to remember which APIs use decorator order, and which ones use reverse decorator order.

So, even though in itself there is no good reason for one order over the other, consistency wins because less thinking.  At the least, if they're not going to be in decorator order, the member shouldn't be called "__decorators__".  ;-)

 
But since you will use this and and me probably not, let your idea rule.

For my motivating use case, I actually don't care about the order within a class very much.  Nick's proposal will actually be the reverse of the application order used by my in-class decorators, but I can easily work around that.

Greg Ewing

unread,
Jun 5, 2012, 7:13:33 PM6/5/12
to pytho...@python.org
PJ Eby wrote:
> At the least, if they're
> not going to be in decorator order, the member shouldn't be called
> "__decorators__". ;-)

Obviously it should be called __srotaroced__.

--
Greg

Nick Coghlan

unread,
Jun 5, 2012, 7:38:39 PM6/5/12
to PJ Eby, pytho...@python.org, Terry Reedy
On Wed, Jun 6, 2012 at 9:06 AM, PJ Eby <p...@telecommunity.com> wrote:
>
>
> On Tue, Jun 5, 2012 at 5:31 PM, Terry Reedy <tjr...@udel.edu> wrote:
>>
>> On 6/5/2012 2:26 PM, PJ Eby wrote:
>>> It's for symmetry and straightforward translation with stacked
>>> decorators, i.e. between:
>>>
>>> @deco1
>>> @deco2
>>> [declaration]
>>>
>>> and __decorators__ = [deco1, deco2]
>>>
>>> Doing it the other way now means a different order for people to
>>> remember; there should be One Obvious Order for decorators, and the one
>>> we have now is it.
>>
>>
>> You and I have different ideas of 'obvious' in this context.
>
>
> To be clearer: I've written other APIs which take multiple decorators, or
> things like decorators that just happen to be a pipeline of functions to be
> applied, and every time the question of what order to put the API in, I
> always put them in this order because then in order to remember what the
> order was, I just have to think of decorators.  This is easier than trying
> to remember which APIs use decorator order, and which ones use reverse
> decorator order.
>
> So, even though in itself there is no good reason for one order over the
> other, consistency wins because less thinking.  At the least, if they're not
> going to be in decorator order, the member shouldn't be called
> "__decorators__".  ;-)

Yeah, I can actually make decent arguments in favour of either order,
but it was specifically "same order as lexical decorators" that tipped
the balance in favour of the approach I wrote up in the PEP.

It's also more consistent given how the base classes are walked. While
I'm not proposing to calculate it this way, you can look at the scheme
the PEP as:

# Walk the MRO to build a complete decorator list
decorators = []
for entry in cls.mro():
decorators.extend(cls.__dict__.get("__decorators__", ())
# Apply the decorators in "Last In, First Out" order, just like
unwinding a chain of super() calls
for deco in reversed(decorators):
cls = deco(cls)

Cheers,
Nick.

--
Nick Coghlan   |   ncog...@gmail.com   |   Brisbane, Australia

Tres Seaver

unread,
Jun 5, 2012, 8:53:44 PM6/5/12
to pytho...@python.org
Or, to make it obvious we are treating 'decorators' as a stack::

while decorators:
cls = decorators.pop()(cls)


--
===================================================================
Tres Seaver +1 540-429-0999 tse...@palladion.com
Palladion Software "Excellence by Design" http://palladion.com

R. David Murray

unread,
Jun 5, 2012, 9:53:22 PM6/5/12
to pytho...@python.org
I don't think about data structures lexically, though, I think of them
programmatically. So I'm with Terry here, I would expect them to be in
the list in the order they are going to get applied. I can see the
other argument, though, and presumably other people's brains work
differently and they'd be more confused by non-lexical ordering.

> It's also more consistent given how the base classes are walked. While
> I'm not proposing to calculate it this way, you can look at the scheme
> the PEP as:
>
> # Walk the MRO to build a complete decorator list
> decorators = []
> for entry in cls.mro():
> decorators.extend(cls.__dict__.get("__decorators__", ())
> # Apply the decorators in "Last In, First Out" order, just like
> unwinding a chain of super() calls
> for deco in reversed(decorators):
> cls = deco(cls)

Assuming I got this right (no guarantees :), the following is actually
easier for me to understand (I had to think to understand what "just
like unwinding a chain of super() calls" meant):

# Walk the MRO from the root, applying the decorators.
for entry in reversed(cls.mro()):
for deco in cls.__dict__.get("__decorators__", ()):
cls = deco(cls)

--David
Reply all
Reply to author
Forward
0 new messages