> On 13 Dec 2019, at 14:57, Arthur Pastel <arthur.pas...@gmail.com> wrote:
> 
> 
>> I think the simpler route to go there (allow the use of `self.attr=` until 
>> after ___post_init__ is run is simply
>> to add another flag attribute, that tells wether "initialization is over", 
>> and respect that flag
> 
> 
> We discussed this just before and having an extra instance attribute was 
> quite problematic.
> That's why I suggested to have an attribute set in the beginning of the 
> __init__ and deleted after the initialization is complete.
> 
> Anyways in both cases, there is still a problem when  the class uses 
> __slots__as @Eric V. Smith mentioned previously.

Hack warning:

Put the flag outside of the class? Say by putting the instance id() into a 
set().
Now it works for slots, just need to think about data races...

Barry


> 
> 
>> On Fri, Dec 13, 2019 at 2:50 PM Joao S. O. Bueno <jsbu...@python.org.br> 
>> wrote:
>> I think the simpler route to go there (allow the use of `self.attr=` until 
>> after ___post_init__ is run is simply
>> to add another flag attribute, that tells wether "initialization is over", 
>> and respect that flag
>> in the added `__setattr__`. The hook calling `__post_init__` would then set 
>> that flag after it is done.
>> 
>> A 5 line change is enough to make this work for __post_init__:
>> 
>> ```
>> diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py 
>> index 91c1f6f80f..65d51ca0a9 100644 
>> --- a/Lib/dataclasses.py 
>> +++ b/Lib/dataclasses.py 
>> @@ -194,6 +194,11 @@ _PARAMS = '__dataclass_params__' 
>> # __init__. 
>> _POST_INIT_NAME = '__post_init__' 
>>  
>> + 
>> +# Flag used to allow field initialization in `__post_init__` 
>> +# and `__init__` for frozen classes 
>> +_FROZEN_INITIALIZATION_FLAG = '__dataclass_initialized__' 
>> + 
>> # String regex that string annotations for ClassVar or InitVar must match. 
>> # Allows "identifier.identifier[" or "identifier[". 
>> # https://bugs.python.org/issue33453 for details. 
>> @@ -517,6 +522,9 @@ def _init_fn(fields, frozen, has_post_init, self_name): 
>>                               if f._field_type is _FIELD_INITVAR) 
>>         body_lines.append(f'{self_name}.{_POST_INIT_NAME}({params_str})') 
>>  
>> +    if frozen: 
>> +        body_lines.append(f'{self_name}.{_FROZEN_INITIALIZATION_FLAG} = 
>> True') 
>> + 
>>     # If no body lines, use 'pass'. 
>>     if not body_lines: 
>>         body_lines = ['pass'] 
>> @@ -552,13 +560,13 @@ def _frozen_get_del_attr(cls, fields): 
>>         fields_str = '()' 
>>     return (_create_fn('__setattr__', 
>>                       ('self', 'name', 'value'), 
>> -                      (f'if type(self) is cls or name in {fields_str}:', 
>> +                      (f'if (type(self) is cls or name in {fields_str}) and 
>> getattr(self, "{_FROZEN_INITIALIZATION_F
>> LAG}", False):', 
>>                         ' raise FrozenInstanceError(f"cannot assign to field 
>> {name!r}")', 
>>                        f'super(cls, self).__setattr__(name, value)'), 
>>                        globals=globals), 
>>             _create_fn('__delattr__', 
>>                       ('self', 'name'), 
>> -                      (f'if type(self) is cls or name in {fields_str}:', 
>> +                      (f'if (type(self) is cls or name in {fields_str}) and 
>> getattr(self, "{_FROZEN_INITIALIZATION_F
>> LAG}", False):', 
>>                         ' raise FrozenInstanceError(f"cannot delete field 
>> {name!r}")', 
>>                        f'super(cls, self).__delattr__(name)'), 
>>                        globals=globals), 
>> (END)
>> ```
>> 
>> To get it working for `__init__` as well, however is a bit more complicated 
>> - as we
>> don't have control of the dataclass metaclass, the only way to put a hook to 
>> set the
>> flag after __init__ is run is to apply a decorator in the existing __init__ 
>> that would
>> do it. 
>> 
>> However, this might be a way to have this feature _IF_ people think it would
>> be worth it - (I personally would prefer yet another parameter to allow 
>> changing 
>> fields during frozen initialization, as that is, for me, an exceptional way 
>> of doing it
>> less simple than having to call `object.__setattr__`)
>> 
>>> On Fri, 13 Dec 2019 at 07:22, Arthur Pastel <arthur.pas...@gmail.com> wrote:
>>>> On Thu, Dec 12, 2019 at 4:37 PM Eric V. Smith <e...@trueblade.com> wrote:
>>>> On 12/12/2019 8:50 AM, Arthur Pastel wrote:
>>>>>>> On 12/12/2019 8:07 AM, Arthur Pastel wrote:
>>>>>>>>> On Thu, Dec 12, 2019 at 2:03 AM Eric V. Smith <e...@trueblade.com> 
>>>>>>>>> wrote:
>>>>>>>>>> On 12/11/2019 6:36 PM, Arthur Pastel wrote:
>>>>>>>>>> >> Add an extra hidden attribute to every instance just
>>>>>>>>>> >> to track whether you’re inside __post_init__ so __setattr__ can 
>>>>>>>>>> >> check it?
>>>>>>>>>> I don't want to add attributes that weren't defined in the class 
>>>>>>>>>> definition.
>>>>>>>>> Would it be okay if we create an instance attribute before calling 
>>>>>>>>> the __init__ and then we remove it after the __post_init__ ?
>>>>>>>>> Then we would simply have to check for the attribute existence when 
>>>>>>>>> checking if we raise a FrozenInstanceError.
>>>>>>>> 
>>>>>>>> That wouldn't work if the class used __slots__.
>>>>>>>> 
>>>>>>> 
>>>>>>>  Then, in this specific case, would it be possible to add the attribute 
>>>>>>> to the __slots__ in the generated class ?
>>>>>>> 
>>>>>> No, __slots__ can't be added or modified after the class exists (which 
>>>>>> is why @dataclass doesn't have an option to add __slots__).
>>>>>> 
>>>>> 
>>>>> Arg, okay i didn't know.
>>>>> Then would it be possible to return a child class in the _process_class 
>>>>> function ? It would then be possible to modify the slots.
>>>>> I'm trying to find a way around this but maybe this is not the good 
>>>>> approach.
>>>>> 
>>>> Yes, it is possible. In my github repo I have an add_slots decorator that 
>>>> does just that. But I don't want the stdlib dataclass decorator to start 
>>>> returning a different class, without some serious discussion and some 
>>>> upsides. I don't think making __post_init__ prettier for frozen classes 
>>>> meets that bar. Although if there's overwhelming support for it here, I 
>>>> could be convinced to change my mind.
>>>> 
>>> 
>>> Then what about at least allowing assignment for classes not having 
>>> __slots__ ?
>>> _______________________________________________
>>> Python-ideas mailing list -- python-ideas@python.org
>>> To unsubscribe send an email to python-ideas-le...@python.org
>>> https://mail.python.org/mailman3/lists/python-ideas.python.org/
>>> Message archived at 
>>> https://mail.python.org/archives/list/python-ideas@python.org/message/SGMX3P352PYYJYLZYZZTQRG6UDVYK5IE/
>>> Code of Conduct: http://python.org/psf/codeofconduct/
> _______________________________________________
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at 
> https://mail.python.org/archives/list/python-ideas@python.org/message/FIKYAHWRJDV2VVTKHG5KLYFF3YSX5T46/
> Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/S7OFQAOZHRNZLPQHV6VTEUHDBJDNYC7A/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to