>
> 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 <e...@trueblade.com> mentioned previously.


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/

Reply via email to