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

Joao S. O. Bueno jsbueno at python.org.br
Thu Jul 28 10:06:28 EDT 2016

On 28 July 2016 at 10:53, Nick Coghlan <ncoghlan at gmail.com> wrote:
> On 28 July 2016 at 23:12, Joao S. O. Bueno <jsbueno at python.org.br> wrote:
>>  Although I know it is not straightforward to implement (as the
>> "metaclass" parameter is not passed to the metaclass __new__ or
>> __init__), wouldn't it make sense to make it be passed to
>> __init_subclass__ just like all other keywords?  (the default
>> __init_subclass__ then could swallow it, if it reaches there).
>> I am putting the question now, because it is a matter of "now" or
>> "never" - I can see it can does make sense if it is not passed down.
> It would complicate the implementation, and has the potential to be
> confusing (since the explicit metaclass hint and the actual metaclass
> aren't guaranteed to be the same), so I don't think it makes sense to
> pass it down. I'll make sure we note it in the documentation for the
> new protocol method, though.
>> Anyway, do you have any remarks on the first issue I raised? About
>> __init_subclass__ being called in the class it is defined in, not just
>> on it's descendant subclasses?
> That's already covered in the PEP:
> https://www.python.org/dev/peps/pep-0487/#calling-the-hook-on-the-class-itself
> We want to make it easy for mixin classes to use __init_subclass__ to
> define "required attributes" on subclasses, and that's straightforward
> with the current definition:
>     class MyMixin:
>         def __init_subclass__(cls, **kwargs):
>             super().__init_subclass__(**kwargs)
>             if not hasattr(cls, "mixin_required_attribute"):
>                 raise TypeError(f"Subclasses of {__class__} must
> define a 'mixin_required_attribute' attribute")
> If you actually do want the init_subclass__ method to also run on the
> "base" class, then you'll need to tweak the class hierarchy a bit to
> look like:
>     class _PrivateInitBase:
>         def __init_subclass__(cls, **kwargs):
>             super().__init_subclass__(**kwargs)
>             ...
>     def MyOriginalClass(_PrivateInitBase):
>         ...
> (You don't want to call __init_subclass__ from a class decorator, as
> that would call any parent __init_subclass__ implementations a second
> time)
> By contrast, when we had the default the other way around, opting
> *out* of self-application required boilerplate inside of
> __init_subclass__ to special case the situation where "cls is
> __class__":
>     class MyMixin:
>         def __init_subclass__(cls, **kwargs):
>             super().__init_subclass__(**kwargs)
>             if cls is __class__:
>                 return # Don't init the base class
>             if not getattr(cls, "mixin_required_attribute", None) is None:
>                 raise TypeError(f"Subclasses of {__class__} must
> define a non-None 'mixin_required_attribute' attribute")
> This raises exciting new opportunities for subtle bugs, like bailing
> out *before* calling the parent __init_subclass__ method, and then
> figure out that a later error from an apparently unrelated method is
> because your __init_subclass__ implementation is buggy.
> There's still an opportunity for bugs with the current design decision
> (folks expecting __init_subclass__ to be called on the class defining
> it when that isn't the case), but they should be relatively shallow
> ones, and once people learn the rule that __init_subclass__ is only
> called on *strict* subclasses, it's a pretty easy behaviour to
> remember.

Thanks for the extensive reasoning.

Maybe then adding a  `run_init_subclass` class decorator on the stdlib
to go along with the pep?
It should be a two liner that would avoid boiler plate done wrong -
but more important than thatm is that it being documented alog with
the __init_sublass__ method will make it more obvious it is not run
where it is defined. (I had missed it in the PEP text and just
understood that part when re-reading the PEP after being surprised)


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

More information about the Python-Dev mailing list