PEP 422: Make __autodecorate__ a true decorator

* __autodecorate__ is now defined as a true decorator that can
  replace the class object entirely
* question whether the namespace parameter is sufficiently
  valuable to be worth the extra complexity, or if Eric Snow's
  idea of using an ordered dict by default would suffice
This commit is contained in:
Nick Coghlan 2015-02-23 21:34:36 +10:00
parent 50b92eab1d
commit 4cf8410bc9
1 changed files with 90 additions and 6 deletions

View File

@ -129,6 +129,18 @@ class initialisation hook as follows::
# The usual super() mechanisms are used to correctly support
# multiple inheritance. The class decorator style signature helps
# ensure that invoking the parent class is as simple as possible.
cls = super().__autodecorate__()
return cls
To simplify the cooperative multiple inheritance case, ``object`` will gain
a default implementation of the hook that returns the class unmodified:
class object:
def __autodecorate__(cls):
return cls
If a metaclass wishes to block implicit class decoration for some reason, it
must arrange for ``cls.__autodecorate__`` to trigger ``AttributeError``.
If present on the created object, this new hook will be called by the class
creation machinery *after* the ``__class__`` reference has been initialised.
@ -137,9 +149,6 @@ returning the created class object. ``__autodecorate__`` is implicitly
converted to a class method when the class is created (prior to the hook
being invoked).
If a metaclass wishes to block implicit class decoration for some reason, it
must arrange for ``cls.__autodecorate__`` to trigger ``AttributeError``.
Note, that when ``__autodecorate__`` is called, the name of the class is not
yet bound to the new class object. As a consequence, the two argument form
of ``super()`` cannot be used to call methods (e.g., ``super(Example, cls)``
@ -151,7 +160,7 @@ This general proposal is not a new idea (it was first suggested for
inclusion in the language definition `more than 10 years ago`_, and a
similar mechanism has long been supported by `Zope's ExtensionClass`_),
but the situation has changed sufficiently in recent years that
the idea is worth reconsidering.
the idea is worth reconsidering for inclusion as a native language feature.
In addition, the introduction of the metaclass ``__prepare__`` method in PEP
3115 allows a further enhancement that was not possible in Python 2: this
@ -269,6 +278,7 @@ if necessary::
class Example:
def __autodecorate__(cls):
cls = super().__autodecorate__()
# Don't process the base class
if cls is __class__:
return
@ -276,6 +286,58 @@ if necessary::
...
Replacing a class with a different kind of object
-------------------------------------------------
As an implicit decorator, ``__autodecorate__`` is able to relatively easily
replace the defined class with a different kind of object. Technically
custom metaclasses and even ``__new__`` methods can already do this
implicitly, but the decorator model makes such code much easier to understand
and implement.
class BuildDict:
def __autodecorate__(cls):
cls = super().__autodecorate__()
# Don't process the base class
if cls is __class__:
return
# Convert subclasses to ordinary dictionaries
return cls.__dict__.copy()
It's not clear why anyone would ever do this implicitly based on inheritance
rather than just using an explicit decorator, but the possibility seems worth
noting.
Open Questions
==============
Is the ``namespace`` concept worth the extra complexity?
--------------------------------------------------------
Unlike the new ``__autodecorate__`` hook the proposed ``namespace`` keyword
argument is not automatically inherited by subclasses. Given the way this
proposal is currently written , the only way to get a special namespace used
consistently in subclasses is still to write a custom metaclass with a
suitable ``__prepare__`` implementation.
Changing the custom namespace factory to also be inherited would
significantly increase the complexity of this proposal, and introduce a
number of the same potential base class conflict issues as arise with the
use of custom metaclasses.
Eric Snow has put forward a
`separate proposal <https://mail.python.org/pipermail/python-dev/2013-June/127103.html>`__
to instead make the execution namespace for class bodies an ordered dictionary
by default, and capture the class attribute definition order for future
reference as an attribute (e.g. ``__definition_order__``) on the class object.
Eric's suggested approach may be a better choice for a new default behaviour
for type that combines well with the proposed ``__autodecorate__`` hook,
leaving the more complex configurable namespace factory idea to a custom
metaclass like the one shown below.
New Ways of Using Classes
=========================
@ -428,6 +490,26 @@ documents it as an ordinary method, and the current documentation doesn't
explicitly say anything one way or the other).
Making ``__autodecorate__`` implicitly static, like ``__new__``
---------------------------------------------------------------
While it accepts the class to be instantiated as the first argument,
``__new__`` is actually implicitly treated as a static method rather than
as a class method. This allows it to be readily extracted from its
defining class and called directly on a subclass, rather than being
coupled to the class object it is retrieved from.
Such behaviour initially appears to be potentially useful for the
new ``__autodecorate__`` hook, as it would allow ``__autodecorate__``
methods to readily be used as explicit decorators on other classes.
However, that apparent support would be an illusion as it would only work
correctly if invoked on a subclass, in which case the method can just as
readily be retrieved from the subclass and called that way. Unlike
``__new__``, there's no issue with potentially changing method signatures at
different points in the inheritance chain.
Passing in the namespace directly rather than a factory function
----------------------------------------------------------------
@ -443,8 +525,10 @@ Reference Implementation
========================
A reference implementation for ``__autodecorate__`` has been posted to the
`issue tracker`_. It uses the original ``__init_class__`` naming and does
not yet include the new ``namespace`` parameter for ``type.__prepare__``.
`issue tracker`_. It uses the original ``__init_class__`` naming. does not yet
allow the implicit decorator to replace the class with a different object and
does not implement the suggested ``namespace`` parameter for
``type.__prepare__``.
TODO
====