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/C2NMBULJJSYYHL2XO5R26F4YD3OURYOS/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to