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:
Martin Teichmann 2016-07-14 15:36:50 +02:00 committed by Berker Peksag
parent 2493d89091
commit 14c5ca31b6
1 changed files with 31 additions and 19 deletions

View File

@ -58,15 +58,15 @@ into the class creation:
1. An ``__init_subclass__`` hook that initializes 1. An ``__init_subclass__`` hook that initializes
all subclasses of a given class. 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 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:: As an example, the first use case looks as follows::
>>> class QuestBase: >>> class QuestBase:
... # this is implicitly a @classmethod ... # this is implicitly a @classmethod (see below for motivation)
... def __init_subclass__(cls, swallow, **kwargs): ... def __init_subclass__(cls, swallow, **kwargs):
... cls.swallow = swallow ... cls.swallow = swallow
... super().__init_subclass__(**kwargs) ... 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 but the situation has changed sufficiently in recent years that
the idea is worth reconsidering for inclusion. 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. initializer for class attributes, especially if they are descriptors.
Descriptors are defined in the body of a Descriptors are defined in the body of a
class, but they do not know anything about that class, they do not 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) instance.__dict__[self.name] = weakref.ref(value)
# this is the new initializer: # this is the new initializer:
def __set_owner__(self, owner, name): def __set_name__(self, owner, name):
self.name = 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 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. 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 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 ``__init_subclass__`` hook. But this would mean that descriptors can
only be used in classes that have the proper hook, the generic version 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 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 ``object.__init_subclass__``. But given that it is a common mistake
to forget to call ``super()``, it would happen too often that suddenly to forget to call ``super()``, it would happen too often that suddenly
descriptors are not initialized. descriptors are not initialized.
@ -179,7 +193,7 @@ Subclass registration
Especially when writing a plugin system, one likes to register new Especially when writing a plugin system, one likes to register new
subclasses of a plugin baseclass. This can be done as follows:: subclasses of a plugin baseclass. This can be done as follows::
class PluginBase(Object): class PluginBase:
subclasses = [] subclasses = []
def __init_subclass__(cls, **kwargs): def __init_subclass__(cls, **kwargs):
@ -212,7 +226,7 @@ PEP::
else: else:
raise ValueError("value not in range") raise ValueError("value not in range")
def __set_owner__(self, owner, name): def __set_name__(self, owner, name):
self.key = name self.key = name
Implementation Details Implementation Details
@ -234,7 +248,7 @@ and ``type`` defined here inherit from the usual ones::
ns['__init_subclass__'] = classmethod(init) ns['__init_subclass__'] = classmethod(init)
self = super().__new__(cls, name, bases, ns) self = super().__new__(cls, name, bases, ns)
for k, v in self.__dict__.items(): for k, v in self.__dict__.items():
func = getattr(v, '__set_owner__', None) func = getattr(v, '__set_name__', None)
if func is not None: if func is not None:
func(self, k) func(self, k)
super(self, self).__init_subclass__(**kwargs) super(self, self).__init_subclass__(**kwargs)
@ -251,14 +265,14 @@ and ``type`` defined here inherit from the usual ones::
class object(object, metaclass=type): class object(object, metaclass=type):
pass 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 then the ``__init_subclass__``. This means that subclass initializers already
see the fully initialized descriptors. This way, ``__init_subclass__`` users see the fully initialized descriptors. This way, ``__init_subclass__`` users
can fix all descriptors again if this is needed. 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 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 however, such a prevention would be accidental, as it often happens that a call
to ``super()`` is forgotten. 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 initialization process, rendering it impossible to back-port the
proposal to older Python versions. 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 hooks
Other variants of calling the hook -----------------------------------
----------------------------------
Other names for the hook were presented, namely ``__decorate__`` or Other names for the hook were presented, namely ``__decorate__`` or
``__autodecorate__``. This proposal opts for ``__init_subclass__`` as ``__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 while it is not very close to decorators, as it does not return the
class. 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__`` Requiring an explicit decorator on ``__init_subclass__``
-------------------------------------------------------- --------------------------------------------------------