[Python-3000] Adding class decorators to PEP 318
Collin Winter
collinw at gmail.com
Tue May 1 17:52:13 CEST 2007
In talking to Neal Norwitz about this, I don't see a need for a
separate PEP for class decorators; we already have a decorators PEP,
#318. The following is a proposed patch to PEP 318 that adds in class
decorators.
Collin Winter
Index: pep-0318.txt
===================================================================
--- pep-0318.txt (revision 55034)
+++ pep-0318.txt (working copy)
@@ -1,5 +1,5 @@
PEP: 318
-Title: Decorators for Functions and Methods
+Title: Decorators for Functions, Methods and Classes
Version: $Revision$
Last-Modified: $Date$
Author: Kevin D. Smith, Jim Jewett, Skip Montanaro, Anthony Baxter
@@ -9,7 +9,7 @@
Created: 05-Jun-2003
Python-Version: 2.4
Post-History: 09-Jun-2003, 10-Jun-2003, 27-Feb-2004, 23-Mar-2004, 30-Aug-2004,
- 2-Sep-2004
+ 2-Sep-2004, 30-Apr-2007
WarningWarningWarning
@@ -22,24 +22,40 @@
negatives of each form.
+UpdateUpdateUpdate
+==================
+
+In April 2007, this PEP was updated to reflect the evolution of the Python
+community's attitude toward class decorators. Though they had previously
+been rejected as too obscure and with limited use-cases, by mid-2006,
+class decorators had come to be seen as the logical next step, with some
+wondering why they hadn't been included originally. As a result, class
+decorators will ship in Python 2.6.
+
+This PEP has been modified accordingly, with references to class decorators
+injected into the narrative. While some references to the lack of class
+decorators have been left in place to preserve the historical record, others
+have been removed for the sake of coherence.
+
+
Abstract
========
-The current method for transforming functions and methods (for instance,
-declaring them as a class or static method) is awkward and can lead to
-code that is difficult to understand. Ideally, these transformations
-should be made at the same point in the code where the declaration
-itself is made. This PEP introduces new syntax for transformations of a
-function or method declaration.
+The current method for transforming functions, methods and classes (for
+instance, declaring a method as a class or static method) is awkward and
+can lead to code that is difficult to understand. Ideally, these
+transformations should be made at the same point in the code where the
+declaration itself is made. This PEP introduces new syntax for
+transformations of a function, method or class declaration.
Motivation
==========
-The current method of applying a transformation to a function or method
-places the actual transformation after the function body. For large
-functions this separates a key component of the function's behavior from
-the definition of the rest of the function's external interface. For
+The current method of applying a transformation to a function, method or class
+places the actual transformation after the body. For large
+code blocks this separates a key component of the object's behavior from
+the definition of the rest of the object's external interface. For
example::
def foo(self):
@@ -69,14 +85,22 @@
are not as immediately apparent. Almost certainly, anything which could
be done with class decorators could be done using metaclasses, but
using metaclasses is sufficiently obscure that there is some attraction
-to having an easier way to make simple modifications to classes. For
-Python 2.4, only function/method decorators are being added.
+to having an easier way to make simple modifications to classes. The
+following is much clearer than the metaclass-based alternative::
+ @singleton
+ class Foo(object):
+ pass
+Because of the greater ease-of-use of class decorators and the symmetry
+with function and method decorators, class decorators will be included in
+Python 2.6.
+
+
Why Is This So Hard?
--------------------
-Two decorators (``classmethod()`` and ``staticmethod()``) have been
+Two method decorators (``classmethod()`` and ``staticmethod()``) have been
available in Python since version 2.2. It's been assumed since
approximately that time that some syntactic support for them would
eventually be added to the language. Given this assumption, one might
@@ -135,11 +159,16 @@
.. _gareth mccaughan:
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=slrna40k88.2h9o.Gareth.McCaughan%40g.local
-Class decorations seem like an obvious next step because class
+Class decorations seemed like an obvious next step because class
definition and function definition are syntactically similar,
-however Guido remains unconvinced, and class decorators will almost
-certainly not be in Python 2.4.
+however Guido was not convinced of their usefulness, and class
+decorators were not in Python 2.4. `The issue was revisited`_ in March 2006
+and sufficient use-cases were found to justify the inclusion of class
+decorators in Python 2.6.
+.. _The issue was revisited:
+ http://mail.python.org/pipermail/python-dev/2006-March/062942.html
+
The discussion continued on and off on python-dev from February
2002 through July 2004. Hundreds and hundreds of posts were made,
with people proposing many possible syntax variations. Guido took
@@ -147,8 +176,8 @@
place. Subsequent to this, he decided that we'd have the `Java-style`_
@decorator syntax, and this appeared for the first time in 2.4a2.
Barry Warsaw named this the 'pie-decorator' syntax, in honor of the
-Pie-thon Parrot shootout which was occured around the same time as
-the decorator syntax, and because the @ looks a little like a pie.
+Pie-thon Parrot shootout which was occuring around the same time as
+the decorator syntax debate, and because the @ looks a little like a pie.
Guido `outlined his case`_ on Python-dev, including `this piece`_
on some of the (many) rejected forms.
@@ -250,6 +279,19 @@
decorators are near the function declaration. The @ sign makes it clear
that something new is going on here.
+Python 2.6's class decorators work similarly::
+
+ @dec2
+ @dec1
+ class Foo:
+ pass
+
+This is equivalent to::
+
+ class Foo:
+ pass
+ Foo = dec2(dec1(Foo))
+
The rationale for the `order of application`_ (bottom to top) is that it
matches the usual order for function-application. In mathematics,
composition of functions (g o f)(x) translates to g(f(x)). In Python,
@@ -321,7 +363,7 @@
There have been a number of objections raised to this location -- the
primary one is that it's the first real Python case where a line of code
has an effect on a following line. The syntax available in 2.4a3
-requires one decorator per line (in a2, multiple decorators could be
+requires one decorator per line (in 2.4a2, multiple decorators could be
specified on the same line).
People also complained that the syntax quickly got unwieldy when
@@ -330,52 +372,61 @@
were small and thus this was not a large worry.
Some of the advantages of this form are that the decorators live outside
-the method body -- they are obviously executed at the time the function
+the function/class body -- they are obviously executed at the time the object
is defined.
-Another advantage is that a prefix to the function definition fits
+Another advantage is that a prefix to the definition fits
the idea of knowing about a change to the semantics of the code before
-the code itself, thus you know how to interpret the code's semantics
+the code itself. This way, you know how to interpret the code's semantics
properly without having to go back and change your initial perceptions
if the syntax did not come before the function definition.
Guido decided `he preferred`_ having the decorators on the line before
-the 'def', because it was felt that a long argument list would mean that
-the decorators would be 'hidden'
+the 'def' or 'class', because it was felt that a long argument list would mean
+that the decorators would be 'hidden'
.. _he preferred:
http://mail.python.org/pipermail/python-dev/2004-March/043756.html
-The second form is the decorators between the def and the function name,
-or the function name and the argument list::
+The second form is the decorators between the 'def' or 'class' and the object's
+name, or between the name and the argument list::
def @classmethod foo(arg1,arg2):
pass
+
+ class @singleton Foo(arg1, arg2):
+ pass
def @accepts(int,int), at returns(float) bar(low,high):
pass
def foo @classmethod (arg1,arg2):
pass
+
+ class Foo @singleton (arg1, arg2):
+ pass
def bar @accepts(int,int), at returns(float) (low,high):
pass
There are a couple of objections to this form. The first is that it
-breaks easily 'greppability' of the source -- you can no longer search
+breaks easy 'greppability' of the source -- you can no longer search
for 'def foo(' and find the definition of the function. The second,
more serious, objection is that in the case of multiple decorators, the
syntax would be extremely unwieldy.
The next form, which has had a number of strong proponents, is to have
the decorators between the argument list and the trailing ``:`` in the
-'def' line::
+'def' or 'class' line::
def foo(arg1,arg2) @classmethod:
pass
def bar(low,high) @accepts(int,int), at returns(float):
pass
+
+ class Foo(object) @singleton:
+ pass
Guido `summarized the arguments`_ against this form (many of which also
apply to the previous form) as:
@@ -403,15 +454,19 @@
@accepts(int,int)
@returns(float)
pass
+
+ class Foo(object):
+ @singleton
+ pass
The primary objection to this form is that it requires "peeking inside"
-the method body to determine the decorators. In addition, even though
-the code is inside the method body, it is not executed when the method
+the suite body to determine the decorators. In addition, even though
+the code is inside the suite body, it is not executed when the code
is run. Guido felt that docstrings were not a good counter-example, and
that it was quite possible that a 'docstring' decorator could help move
the docstring to outside the function body.
-The final form is a new block that encloses the method's code. For this
+The final form is a new block that encloses the function or clas. For this
example, we'll use a 'decorate' keyword, as it makes no sense with the
@syntax. ::
@@ -425,9 +480,14 @@
returns(float)
def bar(low,high):
pass
+
+ decorate:
+ singleton
+ class Foo(object):
+ pass
This form would result in inconsistent indentation for decorated and
-undecorated methods. In addition, a decorated method's body would start
+undecorated code. In addition, a decorated object's body would start
three indent levels in.
@@ -444,6 +504,10 @@
@returns(float)
def bar(low,high):
pass
+
+ @singleton
+ class Foo(object):
+ pass
The major objections against this syntax are that the @ symbol is
not currently used in Python (and is used in both IPython and Leo),
@@ -461,6 +525,10 @@
|returns(float)
def bar(low,high):
pass
+
+ |singleton
+ class Foo(object):
+ pass
This is a variant on the @decorator syntax -- it has the advantage
that it does not break IPython and Leo. Its major disadvantage
@@ -476,6 +544,10 @@
[accepts(int,int), returns(float)]
def bar(low,high):
pass
+
+ [singleton]
+ class Foo(object):
+ pass
The major objection to the list syntax is that it's currently
meaningful (when used in the form before the method). It's also
@@ -490,6 +562,10 @@
<accepts(int,int), returns(float)>
def bar(low,high):
pass
+
+ <singleton>
+ class Foo(object):
+ pass
None of these alternatives gained much traction. The alternatives
which involve square brackets only serve to make it obvious that the
@@ -659,7 +735,10 @@
.. _subsequently rejected:
http://mail.python.org/pipermail/python-dev/2004-September/048518.html
+For Python 2.6, the Python grammar and compiler were modified to allow
+class decorators in addition to function and method decorators.
+
Community Consensus
-------------------
More information about the Python-3000
mailing list