[Python-Dev] PEP487: Simpler customization of class creation

Nick Coghlan ncoghlan at gmail.com
Sun Jul 3 03:57:33 EDT 2016


On 2 July 2016 at 10:50, Martin Teichmann <lkb.teichmann at gmail.com> wrote:
> Hi list,
>
> so this is the next round for PEP 487. During the last round, most of
> the comments were in the direction that a two step approach for
> integrating into Python, first in pure Python, later in C, was not a
> great idea and everything should be in C directly. So I implemented it
> in C, put it onto the issue tracker here:
> http://bugs.python.org/issue27366, and also modified the PEP
> accordingly.
>
> For those who had not been in the discussion, PEP 487 proposes to add
> two hooks, __init_subclass__ which is a classmethod called whenever a
> class is subclassed, and __set_owner__, a hook in descriptors which
> gets called once the class the descriptor is part of is created.

I'm +1 for this part of the proposal.

One potential documentation issue is that __init_subclass__ adds yet a
third special magic method behaviour:

- __new__ is implicitly a static method
- __prepare__ isn't implicitly anything (but in hindsight should have
implicitly been a class method)
- __init_subclass__ is implicitly a class method

I think making __init_subclass__ implicitly a class method is still
the right thing to do if this proposal gets accepted, we'll just want
to see if we can do something to tidy up that aspect of the
documentation at the same time.

> While implementing PEP 487 I realized that there is and oddity in the
> type base class: type.__init__ forbids to use keyword arguments, even
> for the usual three arguments it has (name, base and dict), while
> type.__new__ allows for keyword arguments. As I plan to forward any
> keyword arguments to the new __init_subclass__, I stumbled over that.
> As I write in the PEP, I think it would be a good idea to forbid using
> keyword arguments for type.__new__ as well. But if people think this
> would be to big of a change, it would be possible to do it
> differently.

I *think* I'm in favour of cleaning this up, but I also think the
explanation of the problem with the status quo could stand to be
clearer, as could the proposed change in behaviour. Some example code
at the interactive prompt may help with that.

Positional arguments already either work properly, or give a helpful
error message:

    >>> type("Example", (), {})
    <class '__main__.Example'>
    >>> type.__new__("Example", (), {})
    Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
    TypeError: type.__new__(X): X is not a type object (str)
    >>> type.__new__(type, "Example", (), {})
    <class '__main__.Example'>
    >>> type.__init__("Example", (), {})
    Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
    TypeError: descriptor '__init__' requires a 'type' object but
received a 'str'
    >>> type.__init__(type, "Example", (), {})

By contrast, attempting to use keyword arguments is a fair collection
of implementation defined "Uh, what just happened?":

    >>> type(name="Example", bases=(), dict={})
    Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
    TypeError: type.__init__() takes no keyword arguments
    >>> type.__new__(name="Example", bases=(), dict={}) # Huh?
    Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
    TypeError: type.__new__(): not enough arguments
    >>> type.__new__(type, name="Example", bases=(), dict={})
    <class '__main__.Example'>
    >>> type.__init__(name="Example", bases=(), dict={}) # Huh?
    Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
    TypeError: descriptor '__init__' of 'type' object needs an argument
    >>> type.__init__(type, name="Example", bases=(), dict={}) # Huh?
    Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
    TypeError: type.__init__() takes no keyword arguments

I think the PEP could be accepted without cleaning this up, though -
it would just mean __init_subclass__ would see the "name", "bases" and
"dict" keys when someone attempted to use keyword arguments with the
dynamic type creation APIs.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-Dev mailing list