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:
parent
50b92eab1d
commit
4cf8410bc9
96
pep-0422.txt
96
pep-0422.txt
|
@ -129,6 +129,18 @@ class initialisation hook as follows::
|
||||||
# The usual super() mechanisms are used to correctly support
|
# The usual super() mechanisms are used to correctly support
|
||||||
# multiple inheritance. The class decorator style signature helps
|
# multiple inheritance. The class decorator style signature helps
|
||||||
# ensure that invoking the parent class is as simple as possible.
|
# 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
|
If present on the created object, this new hook will be called by the class
|
||||||
creation machinery *after* the ``__class__`` reference has been initialised.
|
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
|
converted to a class method when the class is created (prior to the hook
|
||||||
being invoked).
|
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
|
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
|
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)``
|
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
|
inclusion in the language definition `more than 10 years ago`_, and a
|
||||||
similar mechanism has long been supported by `Zope's ExtensionClass`_),
|
similar mechanism has long been supported by `Zope's ExtensionClass`_),
|
||||||
but the situation has changed sufficiently in recent years that
|
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
|
In addition, the introduction of the metaclass ``__prepare__`` method in PEP
|
||||||
3115 allows a further enhancement that was not possible in Python 2: this
|
3115 allows a further enhancement that was not possible in Python 2: this
|
||||||
|
@ -269,6 +278,7 @@ if necessary::
|
||||||
|
|
||||||
class Example:
|
class Example:
|
||||||
def __autodecorate__(cls):
|
def __autodecorate__(cls):
|
||||||
|
cls = super().__autodecorate__()
|
||||||
# Don't process the base class
|
# Don't process the base class
|
||||||
if cls is __class__:
|
if cls is __class__:
|
||||||
return
|
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
|
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).
|
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
|
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
|
A reference implementation for ``__autodecorate__`` has been posted to the
|
||||||
`issue tracker`_. It uses the original ``__init_class__`` naming and does
|
`issue tracker`_. It uses the original ``__init_class__`` naming. does not yet
|
||||||
not yet include the new ``namespace`` parameter for ``type.__prepare__``.
|
allow the implicit decorator to replace the class with a different object and
|
||||||
|
does not implement the suggested ``namespace`` parameter for
|
||||||
|
``type.__prepare__``.
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
Loading…
Reference in New Issue