Update PEP 487 (#53)
* Update PEP 487 This adds some changes proposed by Guido. * removed outdated comment * rename __set_owner__ to __set_name__
This commit is contained in:
parent
2493d89091
commit
14c5ca31b6
50
pep-0487.txt
50
pep-0487.txt
|
@ -58,15 +58,15 @@ into the class creation:
|
|||
|
||||
1. An ``__init_subclass__`` hook that initializes
|
||||
all subclasses of a given class.
|
||||
2. upon class creation, a ``__set_owner__`` hook is called on all the
|
||||
2. upon class creation, a ``__set_name__`` hook is called on all the
|
||||
attribute (descriptors) defined in the class, and
|
||||
|
||||
The third category is the topic of another PEP 520.
|
||||
The third category is the topic of another PEP, PEP 520.
|
||||
|
||||
As an example, the first use case looks as follows::
|
||||
|
||||
>>> class QuestBase:
|
||||
... # this is implicitly a @classmethod
|
||||
... # this is implicitly a @classmethod (see below for motivation)
|
||||
... def __init_subclass__(cls, swallow, **kwargs):
|
||||
... cls.swallow = swallow
|
||||
... super().__init_subclass__(**kwargs)
|
||||
|
@ -89,7 +89,7 @@ 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 for inclusion.
|
||||
|
||||
The second part of the proposal adds an ``__set_owner__``
|
||||
The second part of the proposal adds an ``__set_name__``
|
||||
initializer for class attributes, especially if they are descriptors.
|
||||
Descriptors are defined in the body of a
|
||||
class, but they do not know anything about that class, they do not
|
||||
|
@ -116,9 +116,23 @@ referenced values::
|
|||
instance.__dict__[self.name] = weakref.ref(value)
|
||||
|
||||
# this is the new initializer:
|
||||
def __set_owner__(self, owner, name):
|
||||
def __set_name__(self, owner, name):
|
||||
self.name = name
|
||||
|
||||
Such a ``WeakAttribute`` may, for example, be used in a tree structure
|
||||
where one wants to avoid cyclic references via the parent::
|
||||
|
||||
class TreeNode:
|
||||
parent = WeakAttribute()
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
Note that the ``parent`` attribute is used like a normal attribute,
|
||||
yet the tree contains no cyclic references and can thus be easily
|
||||
garbage collected when out of use. The ``parent`` attribute magically
|
||||
becomes ``None`` once the parent ceases existing.
|
||||
|
||||
While this example looks very trivial, it should be noted that until
|
||||
now such an attribute cannot be defined without the use of a metaclass.
|
||||
And given that such a metaclass can make life very hard, this kind of
|
||||
|
@ -128,7 +142,7 @@ Initializing descriptors could simply be done in the
|
|||
``__init_subclass__`` hook. But this would mean that descriptors can
|
||||
only be used in classes that have the proper hook, the generic version
|
||||
like in the example would not work generally. One could also call
|
||||
``__set_owner__`` from whithin the base implementation of
|
||||
``__set_name__`` from whithin the base implementation of
|
||||
``object.__init_subclass__``. But given that it is a common mistake
|
||||
to forget to call ``super()``, it would happen too often that suddenly
|
||||
descriptors are not initialized.
|
||||
|
@ -179,7 +193,7 @@ Subclass registration
|
|||
Especially when writing a plugin system, one likes to register new
|
||||
subclasses of a plugin baseclass. This can be done as follows::
|
||||
|
||||
class PluginBase(Object):
|
||||
class PluginBase:
|
||||
subclasses = []
|
||||
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
|
@ -212,7 +226,7 @@ PEP::
|
|||
else:
|
||||
raise ValueError("value not in range")
|
||||
|
||||
def __set_owner__(self, owner, name):
|
||||
def __set_name__(self, owner, name):
|
||||
self.key = name
|
||||
|
||||
Implementation Details
|
||||
|
@ -234,7 +248,7 @@ and ``type`` defined here inherit from the usual ones::
|
|||
ns['__init_subclass__'] = classmethod(init)
|
||||
self = super().__new__(cls, name, bases, ns)
|
||||
for k, v in self.__dict__.items():
|
||||
func = getattr(v, '__set_owner__', None)
|
||||
func = getattr(v, '__set_name__', None)
|
||||
if func is not None:
|
||||
func(self, k)
|
||||
super(self, self).__init_subclass__(**kwargs)
|
||||
|
@ -251,14 +265,14 @@ and ``type`` defined here inherit from the usual ones::
|
|||
class object(object, metaclass=type):
|
||||
pass
|
||||
|
||||
In this code, first the ``__set_owner__`` are called on the descriptors, and
|
||||
In this code, first the ``__set_name__`` are called on the descriptors, and
|
||||
then the ``__init_subclass__``. This means that subclass initializers already
|
||||
see the fully initialized descriptors. This way, ``__init_subclass__`` users
|
||||
can fix all descriptors again if this is needed.
|
||||
|
||||
Another option would have been to call ``__set_owner__`` in the base
|
||||
Another option would have been to call ``__set_name__`` in the base
|
||||
implementation of ``object.__init_subclass__``. This way it would be possible
|
||||
event to prevent ``__set_owner__`` from being called. Most of the times,
|
||||
even to prevent ``__set_name__`` from being called. Most of the times,
|
||||
however, such a prevention would be accidental, as it often happens that a call
|
||||
to ``super()`` is forgotten.
|
||||
|
||||
|
@ -309,14 +323,9 @@ The original proposal also made major changes in the class
|
|||
initialization process, rendering it impossible to back-port the
|
||||
proposal to older Python versions.
|
||||
|
||||
More importantly, having a pure Python implementation allows us to
|
||||
take two preliminary steps before before we actually change the
|
||||
interpreter, giving us the chance to iron out all possible wrinkles
|
||||
in the API.
|
||||
|
||||
|
||||
Other variants of calling the hook
|
||||
----------------------------------
|
||||
Other variants of calling the hooks
|
||||
-----------------------------------
|
||||
|
||||
Other names for the hook were presented, namely ``__decorate__`` or
|
||||
``__autodecorate__``. This proposal opts for ``__init_subclass__`` as
|
||||
|
@ -324,6 +333,9 @@ it is very close to the ``__init__`` method, just for the subclass,
|
|||
while it is not very close to decorators, as it does not return the
|
||||
class.
|
||||
|
||||
For the ``__set_name__`` hook other names have been proposed as well,
|
||||
``__set_owner__``, ``__set_ownership__`` and ``__init_descriptor__``.
|
||||
|
||||
|
||||
Requiring an explicit decorator on ``__init_subclass__``
|
||||
--------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue