Re: [Python-Dev] Dataclasses, frozen and __post_init__
On Sat, Feb 17, 2018 at 6:40 PM, Guido van Rossum wrote: > > > That's a pretty tricky proposal, and one that's been debated on and off > for a long time in other contexts. And that flag would somehow have to be > part of every instance's state. > > In general the right way to initialize an immutable/frozen object is not > through __init__ but through __new__ -- have you tried that? > Constructing it throught __new__ doesn't actually work as it has no way to alter the arguments that are passed into __init__, I think creating a metaclass that overides __call__ is required to acheive the desired result. Although a factory classmethod would acheive similar api. > > Also, a small example that demonstrates your need would do wonders to help > us understand your use case better. > > # unrelated object class NamedObject: @property def name(self) -> str: return "some name" // has may subclasses @dataclass class Item: name: str @dataclass class NamedObjectItem(Item): name: str = field(init=False) obj: NamedObject def __post_init__(self): self.name = self.obj.name This works fine, until I decided them Item and therefore all subclasses should be frozen as no instances are mutated and if they are ever in the future then its a bug. But to do this the following factory method needs to be added: @classmethod def create(cls, obj: NamedObject, *args, **kwargs): return cls(obj.name, obj, *args, **kwargs) This doesn't look that bad but all fields(up to the last field used that would have been used in __post_init__) needs to be declared in the signature. Thanks Ben Lewis ___ Python-Dev mailing list [email protected] https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Dataclasses, frozen and __post_init__
Why can'y you make `name` on `NamedObjectItem` a property that returns ` self.obj.name`? Why store a duplicate copy of the name? PS. I have to ponder why frozen dataclasses don't use `__new__`. On Fri, Feb 16, 2018 at 11:43 PM, Ben Lewis wrote: > On Sat, Feb 17, 2018 at 6:40 PM, Guido van Rossum > wrote: >> >> >> That's a pretty tricky proposal, and one that's been debated on and off >> for a long time in other contexts. And that flag would somehow have to be >> part of every instance's state. >> > >> In general the right way to initialize an immutable/frozen object is not >> through __init__ but through __new__ -- have you tried that? >> > > Constructing it throught __new__ doesn't actually work as it has no way to > alter the arguments that are passed into __init__, I think creating a > metaclass that overides __call__ is required to acheive the desired result. > Although a factory classmethod would acheive similar api. > > >> >> Also, a small example that demonstrates your need would do wonders to >> help us understand your use case better. >> >> > > # unrelated object > class NamedObject: > @property > def name(self) -> str: > return "some name" > > // has may subclasses > @dataclass > class Item: > name: str > > > @dataclass > class NamedObjectItem(Item): > name: str = field(init=False) > obj: NamedObject > > def __post_init__(self): > self.name = self.obj.name > > This works fine, until I decided them Item and therefore all subclasses > should be frozen as no instances are mutated > and if they are ever in the future then its a bug. But to do this the > following factory method needs to be added: > > @classmethod > def create(cls, obj: NamedObject, *args, **kwargs): > return cls(obj.name, obj, *args, **kwargs) > > This doesn't look that bad but all fields(up to the last field used that > would have been used in __post_init__) needs to be declared in the > signature. > > Thanks > Ben Lewis > -- --Guido van Rossum (python.org/~guido) ___ Python-Dev mailing list [email protected] https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] Replacement proposal for PEP 557 Data Classes
Hi, I have been working on a new way to use Python classes as enhanced dict object(I called it Prodict). My solution is (IMHO) more concise and brings more features. So I wanted to make it a PEP until I saw PEP 557 Data Classes. With my proposed enhancement, I can do everything Data Classes can do plus some neat features. Since 3.7b1 is out and already integrates data classes, how do I proceed? Propose a replacement for PEP 557 or prepare a new PEP? Although my approach doesn't use decorators, it can be easily done with decorators as well. You can check my solution here: https://github.com/ramazanpolat/prodict Ramazan Polat ___ Python-Dev mailing list [email protected] https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Dataclasses, frozen and __post_init__
>
> 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.
___
Python-Dev mailing list
[email protected]
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe:
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Dataclasses, frozen and __post_init__
Agreed the __pre_init__ idea is no improvement. I think we're back where
you started -- just use `object.__setattr__` to set the attribute in
`__post_init__`. That's what the PEP says is used by the generated
`__init__`, so I think it is reasonable to copy that pattern. Presumably
the situation doesn't occur that frequently in real code (__post_init__
feels like a last resort hack anyway).
On Sat, Feb 17, 2018 at 5:59 PM, Ben Lewis wrote:
> 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.
>
--
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
[email protected]
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe:
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
