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

nick.coghlan python-checkins at python.org
Tue Jun 5 14:09:40 CEST 2012


http://hg.python.org/peps/rev/0e3606500a26
changeset:   4449:0e3606500a26
user:        Nick Coghlan <ncoghlan at gmail.com>
date:        Tue Jun 05 22:09:20 2012 +1000
summary:
  Add PEP 422: Dynamic Class Decorators

files:
  pep-0422.txt |  168 +++++++++++++++++++++++++++++++++++++++
  1 files changed, 168 insertions(+), 0 deletions(-)


diff --git a/pep-0422.txt b/pep-0422.txt
new file mode 100644
--- /dev/null
+++ b/pep-0422.txt
@@ -0,0 +1,168 @@
+PEP: 422
+Title: Dynamic class decorators
+Version: $Revision$
+Last-Modified: $Date$
+Author: Nick Coghlan <ncoghlan at gmail.com>
+Status: Draft
+Type: Standards Track
+Content-Type: text/x-rst
+Created: 5-Jun-2012
+Post-History: 5-Jun-2012
+
+
+Abstract
+========
+
+Classes currently support two mechanisms for modification of the class at
+definition time: metaclasses and lexical decorators.
+
+Metaclasses can be awkward and challenging to use correctly in conjunction
+with multiple inheritance and lexical decorators don't interact with class
+inheritance at all.
+
+This PEP proposes a new mechanism for dynamic class decoration that
+interacts more cleanly with class inheritance mechanisms.
+
+
+Specification
+=============
+
+This PEP proposes that a new step be added to the class creation process,
+after the metaclass invocation to construct the class instance and before
+the application of lexical decorators.
+
+This step will walk the class MRO in reverse order, looking for
+``__decorators__`` entries in each class dictionary. These entries are
+expected to be iterables that are also walked in reverse order to retrieve
+class decorators that are automatically applied to the class being defined::
+
+        for entry in reversed(cls.mro()):
+            decorators = entry.__dict__.get("__decorators__", ())
+            for deco in reversed(decorators):
+                cls = deco(cls)
+
+This step in the class creation process will be an implicit part of the
+class statement and also part of the behaviour of ``types.new_class()``.
+
+
+Rationale
+=========
+
+When decorator support was added to classes, the lexical decoration syntax
+was copied directly from function decorators::
+
+    @decorator
+    class Example:
+        # Subclasses will not be decorated automatically
+        pass
+
+This mechanism works well, so long as it is considered acceptable that the
+decorator is *not* applied automatically to any subclasses. If it is
+desired that the behaviour be inherited, it is currently necessary to
+make the step up to defining a `custom metaclass`_::
+
+    class DynamicDecorators(type):
+        """Metaclass for dynamic decorator support
+
+        Creates the class normally, then runs through the MRO looking for
+        __decorators__ attributes and applying the contained decorators to
+        the newly created class
+        """
+        def __new__(meta, name, bases, ns):
+            cls = super(DynamicDecorators, meta).__new__(meta, name, bases, ns)
+            for entry in reversed(cls.mro()):
+                decorators = entry.__dict__.get("__decorators__", ())
+                for deco in reversed(decorators):
+                    cls = deco(cls)
+            return cls
+
+    class Example(metaclass=DynamicDecorators):
+        # Subclasses *will* be decorated automatically
+        __decorators__ = [decorator]
+
+The main potential problem with this approach, is that it can place
+significant constraints on the type heirarchy, as it requires that all
+metaclasses used be well behaved with respect to multiple inheritance.
+
+By making dynamic decorators an inherent part of the class creation process,
+many current use cases of metaclasses may be replaced with dynamic decorators
+instead, greatly reducing the likelihood of metaclass conflicts, as well
+as being substantially easier to write correctly in the first place.
+
+
+Design Discussion
+=================
+
+
+Allowing metaclasses to override the dynamic decoration process
+---------------------------------------------------------------
+
+This PEP does not provide a mechanism that allows metaclasses to override the
+dynamic decoration process. If this feature is deemed desirable in the
+future, then it can be added by moving the functionality described in
+this PEP into a new method on the metaclass (for example, ``__decorate__``),
+with ``type`` providing a suitable default implementation that matches
+the behaviour described here.
+
+This PEP chose the simplicity of the current approach, as lexical decorators
+are currently outside the scope of metaclass control, so it seems reasonable
+to pursue the simpler strategy in the absence of a solid use case for
+making this behaviour configurable.
+
+
+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]
+
+    class C:
+        pass
+    C = deco2(deco1(C))
+
+
+Iterating over the MRO in reverse order
+---------------------------------------
+
+The order of iteration over the MRO for decorator application was chosen to
+match the order of actual call *evaluation* when using ``super`` to invoke
+parent class implementations: the first method to run to completion is that
+closest to the base of the class hierarchy.
+    
+
+References
+==========
+
+.. _custom metaclass:
+   https://bitbucket.org/ncoghlan/misc/src/default/pep422.py
+
+
+Copyright
+=========
+
+This document has been placed in the public domain.
+
+
+..
+   Local Variables:
+   mode: indented-text
+   indent-tabs-mode: nil
+   sentence-end-double-space: t
+   fill-column: 70
+   coding: utf-8
+   End:
+

-- 
Repository URL: http://hg.python.org/peps


More information about the Python-checkins mailing list