PEP 422: Rename init_class as autodecorate
This commit is contained in:
parent
d50818e4e8
commit
a9817b6218
88
pep-0422.txt
88
pep-0422.txt
|
@ -21,9 +21,9 @@ creating the potential for spurious metaclass conflicts.
|
||||||
|
|
||||||
This PEP proposes to instead support a wide range of customisation
|
This PEP proposes to instead support a wide range of customisation
|
||||||
scenarios through a new ``namespace`` parameter in the class header, and
|
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
|
implementing a custom metaclass, and thus should provide a gentler
|
||||||
introduction to the full power Python's metaclass machinery.
|
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
|
but potentially without the full flexibility of the Python 2 style
|
||||||
``__metaclass__`` hook
|
``__metaclass__`` hook
|
||||||
|
|
||||||
One mechanism that can achieve this goal is to add a new class
|
One mechanism that can achieve this goal is to add a new implicit class
|
||||||
initialisation hook, modelled directly on the existing instance
|
decoration hook, modelled directly on the existing explicit class
|
||||||
initialisation hook, but with the signature constrained to match that
|
decorators, but defined in the class body or in a parent class, rather than
|
||||||
of an ordinary class decorator.
|
being part of the class definition header.
|
||||||
|
|
||||||
Specifically, it is proposed that class definitions be able to provide a
|
Specifically, it is proposed that class definitions be able to provide a
|
||||||
class initialisation hook as follows::
|
class initialisation hook as follows::
|
||||||
|
|
||||||
class Example:
|
class Example:
|
||||||
def __init_class__(cls):
|
def __autodecorate__(cls):
|
||||||
# This is invoked after the class is created, but before any
|
# This is invoked after the class is created, but before any
|
||||||
# explicit decorators are called
|
# explicit decorators are called
|
||||||
# The usual super() mechanisms are used to correctly support
|
# 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
|
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.
|
||||||
For ``types.new_class()``, it will be called as the last step before
|
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
|
converted to a class method when the class is created (prior to the hook
|
||||||
being invoked).
|
being invoked).
|
||||||
|
|
||||||
If a metaclass wishes to block class initialisation for some reason, it
|
If a metaclass wishes to block implicit class decoration for some reason, it
|
||||||
must arrange for ``cls.__init_class__`` to trigger ``AttributeError``.
|
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
|
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)``
|
||||||
wouldn't work in the example above). However, the zero argument form of
|
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::
|
an ordered dictionary as the class namespace::
|
||||||
|
|
||||||
class OrderedExample(namespace=collections.OrderedDict):
|
class OrderedExample(namespace=collections.OrderedDict):
|
||||||
def __init_class__(cls):
|
def __autodecorate__(cls):
|
||||||
# cls.__dict__ is still a read-only proxy to the class namespace,
|
# cls.__dict__ is still a read-only proxy to the class namespace,
|
||||||
# but the underlying storage is an OrderedDict instance
|
# 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
|
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.
|
*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
|
a similar level of risk to adding an ``__init__`` method: technically, there
|
||||||
is a risk of breaking poorly implemented subclasses, but when that occurs,
|
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
|
it is recognised as a bug in the subclass rather than the library author
|
||||||
breaching backwards compatibility guarantees. In fact, due to the constrained
|
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__``.
|
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,
|
For use cases that don't involve completely replacing the defined class,
|
||||||
Python 2 code that dynamically set ``__metaclass__`` can now dynamically
|
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
|
an explicit metaclass (possibly made available as a required base class) will
|
||||||
still be necessary in order to support Python 3.
|
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
|
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
|
Calling the new hook automatically from ``type.__init__``, would achieve most
|
||||||
of the goals of this PEP. However, using that approach would mean that
|
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
|
relied on the ``__class__`` reference (or used the zero-argument form of
|
||||||
``super()``), and could not make use of those features themselves.
|
``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
|
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
|
sensible interpretation for leaving it out, and that case would need to be
|
||||||
detected anyway in order to give a useful error message.
|
detected anyway in order to give a useful error message.
|
||||||
|
|
||||||
|
@ -393,9 +433,9 @@ the factory function version.
|
||||||
Reference Implementation
|
Reference Implementation
|
||||||
========================
|
========================
|
||||||
|
|
||||||
A reference implementation for __init_class__ has been posted to the
|
A reference implementation for ``__autodecorate__`` has been posted to the
|
||||||
`issue tracker`_. It does not yet include the new ``namespace`` parameter
|
`issue tracker`_. It uses the original ``__init_class__`` naming and does
|
||||||
for ``type.__prepare__``.
|
not yet include the new ``namespace`` parameter for ``type.__prepare__``.
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
Loading…
Reference in New Issue