[Python-Dev] Dataclasses, frozen and __post_init__

Ben Lewis benlewisj at gmail.com
Sat Feb 17 20:59:57 EST 2018


>
> Why can'y you make `name` on `NamedObjectItem` a property that returns `
> self.obj.name`? Why store a duplicate copy of the name?
>

Agreed, it's probably a better design not to store a duplicate reference to
name. But when I tried that, the property clashed with the inherited field.
This caused the creation of the dataclass to fail as it thought that the
property was the default value for the field 'name'. Even if I set a
default for the obj field, it crashed as it tried to set the default value
for name to the read-only property.

Although I can think of situations where properties wouldn't be sufficent
as you only want to calculate the value once per instance on creation. My
thought is that most dataclasses would still be sensible and useful even if
all mutation ability was removed from them. Taking an example directly from
the PEP:

@dataclass
class C:
    i: int
    j: int = None
    database: InitVar[DatabaseType] = None

    def __post_init__(self, database):
        if self.j is None and database is not None:
            self.j = database.lookup('j')

Maybe I'm thinking of dataclasses wrong but this still make complete sense
and is useful even if its declared as frozen.

My thought is that initialisation logic and immutability is orthogonal to
each other. Possibly initialisation logic like this should occur before the
instance is created so it would work for immutable types as well.

A possible idea could be, instead of __post_init__, there is __pre_init__
which allows altering of fields before the instance is created. It would
take a dict as first argument which contain the field values passed into
the 'constructor' and default values would also be filled out.

@dataclass
class C:
    i: int
    j: int = None
    database: InitVar[DatabaseType]

    @classmethod
    def __pre_init__(cls, fields: Dict[str, Any], database: DatabaseType):
        if fields['j'] is None and database is not None:
            fields['j'] = database.lookup('j')

I personally see two problems with this idea:
1. This isn't as ergonomic as __post_init__ is as its modifing a dictionary
instead of its instance.
2. To implement this, it would require a metaclass.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20180218/3bf260ee/attachment.html>


More information about the Python-Dev mailing list