PEP 422: Rename init_class as autodecorate

This commit is contained in:
Nick Coghlan 2015-02-23 00:47:12 +10:00
parent d50818e4e8
commit a9817b6218
1 changed files with 64 additions and 24 deletions

View File

@ -21,9 +21,9 @@ creating the potential for spurious metaclass conflicts.
This PEP proposes to instead support a wide range of customisation
scenarios through a new ``namespace`` parameter in the class header, and
a new ``__init_class__`` hook in the class body.
a new ``__autodecorate__`` hook in the class body.
The new mechanism is also much easier to understand and use than
The new mechanism should be easier to understand and use than
implementing a custom metaclass, and thus should provide a gentler
introduction to the full power Python's metaclass machinery.
@ -109,16 +109,16 @@ added to Python 3.4 that meets the following criteria:
but potentially without the full flexibility of the Python 2 style
``__metaclass__`` hook
One mechanism that can achieve this goal is to add a new class
initialisation hook, modelled directly on the existing instance
initialisation hook, but with the signature constrained to match that
of an ordinary class decorator.
One mechanism that can achieve this goal is to add a new implicit class
decoration hook, modelled directly on the existing explicit class
decorators, but defined in the class body or in a parent class, rather than
being part of the class definition header.
Specifically, it is proposed that class definitions be able to provide a
class initialisation hook as follows::
class Example:
def __init_class__(cls):
def __autodecorate__(cls):
# This is invoked after the class is created, but before any
# explicit decorators are called
# The usual super() mechanisms are used to correctly support
@ -128,14 +128,14 @@ class initialisation hook as follows::
If present on the created object, this new hook will be called by the class
creation machinery *after* the ``__class__`` reference has been initialised.
For ``types.new_class()``, it will be called as the last step before
returning the created class object. ``__init_class__`` is implicitly
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 class initialisation for some reason, it
must arrange for ``cls.__init_class__`` to trigger ``AttributeError``.
If a metaclass wishes to block implicit class decoration for some reason, it
must arrange for ``cls.__autodecorate__`` to trigger ``AttributeError``.
Note, that when ``__init_class__`` 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
of ``super()`` cannot be used to call methods (e.g., ``super(Example, cls)``
wouldn't work in the example above). However, the zero argument form of
@ -158,7 +158,7 @@ created dictionary instance. For example, the following will use
an ordered dictionary as the class namespace::
class OrderedExample(namespace=collections.OrderedDict):
def __init_class__(cls):
def __autodecorate__(cls):
# cls.__dict__ is still a read-only proxy to the class namespace,
# but the underlying storage is an OrderedDict instance
@ -220,12 +220,12 @@ These occur whenever two unrelated metaclasses are used by the desired
parents of a class definition. This risk also makes it very difficult to
*add* a metaclass to a class that has previously been published without one.
By contrast, adding an ``__init_class__`` method to an existing type poses
By contrast, adding an ``__autodecorate__`` method to an existing type poses
a similar level of risk to adding an ``__init__`` method: technically, there
is a risk of breaking poorly implemented subclasses, but when that occurs,
it is recognised as a bug in the subclass rather than the library author
breaching backwards compatibility guarantees. In fact, due to the constrained
signature of ``__init_class__``, the risk in this case is actually even
signature of ``__autodecorate__``, the risk in this case is actually even
lower than in the case of ``__init__``.
@ -243,11 +243,33 @@ Replaces many use cases for dynamic setting of ``__metaclass__``
For use cases that don't involve completely replacing the defined class,
Python 2 code that dynamically set ``__metaclass__`` can now dynamically
set ``__init_class__`` instead. For more advanced use cases, introduction of
set ``__autodecorate__`` instead. For more advanced use cases, introduction of
an explicit metaclass (possibly made available as a required base class) will
still be necessary in order to support Python 3.
Design Notes
============
Determining if the class being decorated is the base class
----------------------------------------------------------
In the body of an ``__autodecorate__`` method, as in any other class method,
``__class__`` will be bound to the class declaring the method, while the
value passed in may be a subclass.
This makes it relatively straightforward to skip processing the base class
if necessary::
class Example:
def __autodecorate__(cls):
# Don't process the base class
if cls is __class__:
return
# Process subclasses here
New Ways of Using Classes
=========================
@ -354,21 +376,39 @@ Rejected Design Options
=======================
Calling ``__init_class__`` from ``type.__init__``
-------------------------------------------------
Calling ``__autodecorate__`` from ``type.__init__``
---------------------------------------------------
Calling the new hook automatically from ``type.__init__``, would achieve most
of the goals of this PEP. However, using that approach would mean that
``__init_class__`` implementations would be unable to call any methods that
``__autodecorate__`` implementations would be unable to call any methods that
relied on the ``__class__`` reference (or used the zero-argument form of
``super()``), and could not make use of those features themselves.
Requiring an explict decorator on ``__init_class__``
----------------------------------------------------
Calling the automatic decoration hook ``__init_class__``
--------------------------------------------------------
Earlier versions of the PEP used the name ``__init_class__`` for the name
of the new hook. There were three significant problems with this name:
* it was hard to remember if the correct spelling was ``__init_class__`` or
``__class_init__``
* the use of "init" in the name suggested the signature should match that
of ``type.__init__``, which is not the case
* the use of "init" in the name suggested the method would be run as part
of class construction, which is not the case
The new name ``__autodecorate__`` was chosen to make it clear that the new
initialisation hook is most usefully thought of as an implicitly invoked
class decorator, rather than as being like an ``__init__`` method.
Requiring an explicit decorator on ``__autodecorate__``
-------------------------------------------------------
Originally, this PEP required the explicit use of ``@classmethod`` on the
``__init_class__`` decorator. It was made implicit since there's no
``__autodecorate__`` decorator. It was made implicit since there's no
sensible interpretation for leaving it out, and that case would need to be
detected anyway in order to give a useful error message.
@ -393,9 +433,9 @@ the factory function version.
Reference Implementation
========================
A reference implementation for __init_class__ has been posted to the
`issue tracker`_. It does not yet include the new ``namespace`` parameter
for ``type.__prepare__``.
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__``.
TODO
====