Improve readability
This commit is contained in:
parent
970e9c5346
commit
49a5c29d8f
86
pep-0487.txt
86
pep-0487.txt
|
@ -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
|
||||||
=======================
|
=======================
|
||||||
|
|
Loading…
Reference in New Issue