diff --git a/pep-0422.txt b/pep-0422.txt index 5d9ce39cd..6e7cb4e56 100644 --- a/pep-0422.txt +++ b/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 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 ====