[Python-ideas] Simpler Customization of Class Creation - PEP 487

Martin Teichmann lkb.teichmann at gmail.com
Mon Feb 8 04:28:17 EST 2016


Hi Nick, Hi List,

Thanks for the good comments, I tried to incorporate all of them
into the new version of my PEP, which can be found here:

https://github.com/tecki/peps/blob/pep487/pep-0487.txt

I'm not sure whether it is a good idea to post the entire PEP
here all the time, but actually your inline comments were
a very helpful technique. I'm not sure how to proceed.

Just some comment about the changes I made:

> Given the three-fold difference in behaviour, naming the new metaclass
> and class after only one of those behaviours seems misleading. On the
> other hand, "OrderedAttributeOwningSubclassInitialisingMeta" would be
> silly, so it might be worth instead calling them something like
> "ProvisionalMeta" and "ProvisionalClass" - that is, you're opting in
> to the provisional future behaviour of the "type" and "object"
> builtins, without specifying exactly what the current differences are.

I had already figured that SubclassInit is not such a great naming idea,
but couldn't find anything better. Calling something Provisional gives
bad Karma, nothing sticks around longer than something marked
"Provisional". So, I propose to call the metaclass "types.Type" and
the base class "types.Object". Maybe we can even go for "types.type"
and "types.object"? I thought this would be a bit, well, blunt, so I chose
the former.

>> To give an example of its usage, imagine a descriptor representing weak
>> referenced values (this is an insanely simplified, yet working example)::
>>
>>     import weakref
>>
>>     class WeakAttribute:
>>         def __get__(self, instance, owner):
>>             return instance.__dict__[self.name]
>>
>>         def __set__(self, instance, value):
>>             instance.__dict__[self.name] = weakref.ref(value)
>>
>>         # this is the new initializer:
>>         def __init_descriptor__(self, owner, name):
>>             self.name = name
>
> Similar to the __subclass_init__ case, a more meaningful usage example
> may help here, such as allowing owning classes to define a hook that
> gets called when the weak reference goes away, while still having
> useful default behaviour. For example (untested):
>
>     class WeakAttribute:
>         def __init__(self):
>             self._owner = None
>             self._name = None
>             self._callback = None
>
>         def __get__(self, instance, owner):
>             if instance is None:
>                 return self
>             return instance.__dict__[self.name]
>
>         def __set__(self, instance, value):
>             instance.__dict__[self.name] = weakref.proxy(value, self._callback)
>
>         def __set_owner__(self, owner, attr):
>             if self._owner is not None:
>                 raise RuntimeError("{!r} already owned by
> {!r}".format(self, self._owner())
>             self._owner = weakref.ref(owner)
>             self._name = attr
>             callback = getattr(owner, "attribute_collected", None)
>             if callback is not None:
>                 self._callback = functools.partial(callback, attr)
>
>     class Example(metaclass.SubclassInit):
>         proxy = WeakAttribute()
>         def attribute_collected(self, attr):
>             print("{} was garbage collected".format())

While I think that you're correct that a better example would be a good
idea, you example for me hints to that __set_owner__ does an initialization
of the attribute at object creation time, but it does it at class creation time.
I'll sleep over it, maybe I find a nice example.

It's also important to note that the new thing about the metaclass is
not that it supplies the attribute with the owner, but that it tell
the attribute
about its name. The owner was already shipped to __get__ and __set__,
but not the name within that class. I am not aware of a way to find out
the attribute name from within a descriptor. (That's not true: a year ago
some ideas how to do that were posted here, but they used undocumented
internal features of CPython, not necessarily a reasonable way).
This is why the name __set_owner__ also might be misleading, but
__set_name__ also doesn't sound right to me, any better calls? For
the meantime I stick with __set_owner__.

>> This used to be a competing proposal to PEP 422 by Nick Coughlan and
>
> Coghlan :)
Sorry...

Greetings

Martin Teichmann


More information about the Python-ideas mailing list