Improve readability

This commit is contained in:
Martin Teichmann 2016-07-16 14:13:04 +02:00 committed by GitHub
parent 970e9c5346
commit 49a5c29d8f
1 changed files with 42 additions and 44 deletions

View File

@ -232,16 +232,50 @@ PEP::
Implementation Details Implementation Details
====================== ======================
For those who prefer reading Python over english, the following is a Python The different hooks are called in the following order: ``type.__new__`` calls
equivalent of the C API changes proposed in this PEP, where the new ``object`` the ``__set_name__`` hooks on the descriptor after the new class has been
and ``type`` defined here inherit from the usual ones:: initialized. Then it calls ``__init_subclass__`` on the base class, on
``super()``, to be precise. 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.
import types Another option would have been to call ``__set_name__`` in the base
implementation of ``object.__init_subclass__``. This way it would be possible
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.
class type(type): Another small change should be done: in the current implementation of
CPython, ``type.__init__`` explicitly forbids the use of keyword arguments,
while ``type.__new__`` allows for its attributes to be shipped as keyword
arguments. This is weirdly incoherent, and thus it should be forbidden.
While it would be possible to retain the current behavior, it would be better
if this was fixed, as it is probably not used at all: the only use case would
be that at metaclass calls its ``super().__new__`` with *name*, *bases* and
*dict* (yes, *dict*, not *namespace* or *ns* as mostly used with modern
metaclasses) as keyword arguments. This should not be done. This little
change simplifies the implementation of this PEP significantly, while
improving the coherence of Python overall.
As a second change, the new ``type.__init__`` just ignores keyword
arguments. Currently, it insists that no keyword arguments are given. This
leads to a (wanted) error if one gives keyword arguments to a class declaration
if the metaclass does not process them. Metaclass authors that do want to
accept keyword arguments must filter them out by overriding ``__init___``.
In the new code, it is not ``__init__`` that complains about keyword arguments,
but ``__init_subclass__``, whose default implementation takes no arguments. In
a classical inheritance scheme using the method resolution order, each
``__init_subclass__`` may take out it's keyword arguments until none are left,
which is checked by the default implementation of ``__init_subclass__``.
For readers who prefer reading Python over English, this PEP proposes to
replace the current ``type`` and ``object`` with the following::
class NewType(type):
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
if len(args) == 1: if len(args) != 3:
return super().__new__(cls, args[0]) return super().__new__(cls, *args)
name, bases, ns = args name, bases, ns = args
init = ns.get('__init_subclass__') init = ns.get('__init_subclass__')
if isinstance(init, types.FunctionType): if isinstance(init, types.FunctionType):
@ -257,47 +291,11 @@ and ``type`` defined here inherit from the usual ones::
def __init__(self, name, bases, ns, **kwargs): def __init__(self, name, bases, ns, **kwargs):
super().__init__(name, bases, ns) super().__init__(name, bases, ns)
class object: class NewObject(object):
@classmethod @classmethod
def __init_subclass__(cls): def __init_subclass__(cls):
pass pass
class object(object, metaclass=type):
pass
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_name__`` in the base
implementation of ``object.__init_subclass__``. This way it would be possible
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.
Another small change should be noted here: in the current implementation of
CPython, ``type.__init__`` explicitly forbids the use of keyword arguments,
while ``type.__new__`` allows for its attributes to be shipped as keyword
arguments. This is weirdly incoherent, and thus the above code forbids that.
While it would be possible to retain the current behavior, it would be better
if this was fixed, as it is probably not used at all: the only use case would
be that at metaclass calls its ``super().__new__`` with *name*, *bases* and
*dict* (yes, *dict*, not *namespace* or *ns* as mostly used with modern
metaclasses) as keyword arguments. This should not be done.
As a second change, the new ``type.__init__`` just ignores keyword
arguments. Currently, it insists that no keyword arguments are given. This
leads to a (wanted) error if one gives keyword arguments to a class declaration
if the metaclass does not process them. Metaclass authors that do want to
accept keyword arguments must filter them out by overriding ``__init___``.
In the new code, it is not ``__init__`` that complains about keyword arguments,
but ``__init_subclass__``, whose default implementation takes no arguments. In
a classical inheritance scheme using the method resolution order, each
``__init_subclass__`` may take out it's keyword arguments until none are left,
which is checked by the default implementation of ``__init_subclass__``.
Rejected Design Options Rejected Design Options
======================= =======================