On 12 Feb 2013 07:44, "Guido van Rossum" <guido@python.org> wrote:
> On Mon, Feb 11, 2013 at 12:57 PM, PJ Eby <pje@telecommunity.com> wrote:
>> On Mon, Feb 11, 2013 at 12:44 PM, Guido van Rossum <guido@python.org> wrote:
>> > Hi Nick,
>> >
>> > I think this will make a fine addition to the language. I agree that
>> > it is superior to the alternatives and fulfills a real (if rare) need.
>> >
>> > I only have a few nits/questions/suggestions.
>> >
>> > - With PJE, I think __init_class__ should automatically be a class
>> > method.
>> Actually, I didn't say that as such, because I'm not sure how the heck
>> we'd implement that.  ;-)
>> For example, at what point is it converted to a classmethod?  Is it
>> going to be a slot method with special C-level handling?  Handled by
>> the compiler?  What happens if somebody makes it a
>> > The same way that __new__ is automatically a class method.
>> Actually, isn't it automatically a staticmethod?  Oh crap.  Now that
>> I'm thinking about it, doesn't this *have* to be a static method,
>> explicitly passing in the class?  I mean, otherwise, won't calling
>> super().__init_class__() invoke it on the base class, rather than the
>> current class?
>> ISTM that EIBTI argues for the __new__/staticmethod approach,
>> especially if you're returning the class (per below)
> Let's see what Nick and the implementer say.

I think these are some interesting ideas and it's going to take me a while to digest them and update the PEP :)

A few random thoughts:

1. I like the idea of a metaprogramming "howto" that provides advice on choosing a suitable level of metaprogramming (with the default choice being "use existing decorators", then escalating through creating custom decorators all the way to creating custom metaclasses). I don't think the PEP needs to be conditional on writing that, but I will at least add PJE's list to the PEP itself.

2. I see the new method as more analogous to__init__ than to__new__, so the __decorate_class__ idea makes me nervous, as it's closer to a __new__ method. Composition gets a *lot* harder when your parent class can switch out the object on you.

3. I'm trying to avoid any custom magic specific to this method, but making it implicitly a static or class method is fairly easy if we so choose - the standard retrieval code during class creation can just bypass the descriptor machinery, and wrap it in staticmethod or classmethod if it isn't already. Given that __new__ is already implicitly static, it may be easier to follow that precedent here rather than trying to explain why an explicit @classmethod is needed in one case but not the other.

4.__class__ is already bound as soon as we have a class object to bind it to, so we can't move it any earlier. However, it's already early enough to let references to it from the new method (including the implied one in zero-arg super) work properly. The black magic that is zero-arg super also deals with PJE's concern about propagating the actual class up the MRO (as it is equivalent to "super(__class__, first_argument)").

5. Implicitly walking the MRO bothers me, as it becomes a special case for people to learn. We don't do that for __init__ or __new__, so I don't think it makes sense to do it here. We can include a recommended structure in the docs, where the first step is calling the parent through. As PJE suggested, a no-op method on type will make that simple and fairly safe (people using a metaclass hierarchy not anchored on type can figure out their own equivalent)


>> > - Would it make any sense to require that __init_class__ *returns* the
>> > new class object (to complete the similarity with class decorators)?
>> It would certainly be quite useful to do so, but in that case, perhaps
>> the method should be named __decorate_class__?  And in that event the
>> standard usage would look like:
>>     def __decorate_class__(cls):
>>         cls = super().__decorate_class__(cls)
>>         # do stuff
>>         return cls
>> On the other hand, one could just drop the super() requirement and
>> make the usage even simpler by having the class machinery walk the MRO
>> and pass each method the result of invoking the previous one.  Then
>> the methods are short and sweet, and super() and __class__ don't come
>> into it.  (Though I guess the class machinery could keep setting
>> __class__ to whatever the last-returned class was.)
>> In his first draft, Nick implemented inheritable decorators instead,
>> using a __decorators__ attribute in the class body, or something like
>> that.  While that approach had an issue or two of its own, it's
>> possible that just going with a single __decorate_class__ method would
>> work out better.
> Half-baked idea: Maybe the base class __decorate_class__ would call the class decorators? Or does that not make sense?
> --
> --Guido van Rossum (python.org/~guido)