> > 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/