[Python-Dev] descriptor __set_name__ and dataclasses

Eric V. Smith eric at trueblade.com
Mon Mar 26 11:10:25 EDT 2018


On 3/26/18 11:08 AM, Nick Coghlan wrote:
> On 27 March 2018 at 00:40, Eric V. Smith <eric at trueblade.com> wrote:
>> https://bugs.python.org/issue33141 points out an interesting issue with
>> dataclasses and descriptors.
>>
>> Given this code:
>>
>> from dataclasses import *
>>
>> class D:
>>     """A descriptor class that knows its name."""
>>     def __set_name__(self, owner, name):
>>         self.name = name
>>     def __get__(self, instance, owner):
>>         if instance is not None:
>>             return 1
>>         return self
>>
>>
>> @dataclass
>> class C:
>>     d: int = field(default=D(), init=False)
>>
>> C.d.name is not set, because d.__set_name__ is never called. However, in
>> this case:
>>
>> class X:
>>     d: int = D()
>>
>> X.d.name is set to 'd' when d.__set_name__ is called during type.__new__.
>>
>> The problem of course, is that in the dataclass case, when class C is
>> initialized, and before the decorator is called, C.d is set to a Field()
>> object, not to D(). It's only when the dataclass decorator is run that I
>> change C.d from a Field to the value of D(). That means that the call to
>> d.__set_name__(C, 'd') is skipped. See
>> https://www.python.org/dev/peps/pep-0487/#implementation-details for details
>> on how type.__new__ works.
>>
>> The only workaround I can think of is to emulate the part of PEP 487 where
>> __set_name__ is called. I can do this from within the @dataclass decorator
>> when I'm initializing C.d. I'm not sure how great this solution is, since
>> it's moving the call from class creation time to class decorator time. I
>> think in 99+% of cases this would be fine, but you could likely write code
>> that depends on side effects of being called during type.__new__.
>>
>> Unless anyone has strong objections, I'm going to make the call to
>> __set_name__ in the @datacalss decorator. Since this is such a niche use
>> case, I don't feel strongly that it needs to be in today's beta release, but
>> if possible I'll get it in. I already have the patch written. And if it does
>> get in but the consensus is that it's a bad idea, we can back it out.
>
> Would it be feasible to define `Field.__set_name__`, and have that
> call `default.__set_name__` when the latter exists, and be a no-op
> otherwise?

A clever idea! I'll look in to it.

Eric.


More information about the Python-Dev mailing list