Make dataclass aware that it might be used with Enum

Perhaps, this has already been addressed in a newer release (?) but in Python 3.9, making `@dataclass` work with `Enum` is a bit awkward. Currently, it order to make it work, I have to: 1. Pass `init=False` to `@dataclass` and hand-write the `__init__` method 2. Pass `repr=False` to `@dataclass` and use `Enum`'s representation or write a custom __repr__ Example: In [72]: @dataclass(frozen=True, init=False, repr=False) ...: class Creature(Enum): ...: legs: int ...: size: str ...: Beetle = (6, 'small') ...: Dog = (4, 'medium') ...: def __init__(self, legs, size): ...: self.legs = legs ...: self.size = size ...: In [73]: Creature.Dog Out[73]: <Creature.Dog: (4, 'medium')>

After some playing around, I figured out a pattern that works without any changes to the implementations of `dataclass` or `Enum`, and I like this because it keeps the 2 kinds of concern separate. Maybe I'll try submitting an MR to add an example like this to the documentation for `Enum`. In [1]: from dataclasses import dataclass In [2]: from enum import Enum In [3]: @dataclass(frozen=True) ...: class CreatureDataMixin: ...: size: str ...: legs: int ...: In [4]: class Creature(CreatureDataMixin, Enum): ...: BEETLE = ('small', 6) ...: DOG = ('medium', 4) ...: In [5]: Creature.DOG Out[5]: Creature(size='medium', legs=4)

On Fri, 8 Jul 2022 at 02:22, Steve Jorgensen <stevecjor@gmail.com> wrote:
I really like this example. I love dataclasses and I love enums and it looks like they go together like peanut butter and chocolate.You get a free initialiser (__init__ method) and all the other goodness of dataclasses (which are really very good). I tweeted your example and it got 29 likes :-) https://twitter.com/voidspace/status/1546832332056924161 Michael
-- Michael Foord Python Consultant, Contractor and Trainer https://agileabstractions.com/

On 7/7/22 18:22, Steve Jorgensen wrote:
I'm impressed that you found a way to make it work. Be aware that some of the bug-fixing in 3.11 has changed the resulting repr() -- the above now looks like: <Creature.DOG: CreatureDataMixin(size='medium', legs=4)> It would be possible to have Enum check to see if the data type is a dataclass, and then see if the repr is set to be automatically created:
CreatureDataMixin.__dataclass_params__ _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=True)
I'll have to look into that. It does seem like a lot of extra work, or at least no less work, than just writing an `__init__` in the enum class directly. -- ~Ethan~

On 7/7/22 09:01, Steve Jorgensen wrote:
Actually, maybe these are fundamentally incompatible?
Their intended use seems fundamentally incompatible: - dataclass was designed for making many mutable records (hundreds, thousands, or more) - enum was designed to make a handful of named constants (I haven't yet seen one with even a hundred elements) The repr from a combined dataclass/enum looks like a dataclass, giving no clue that the object is an enum, and omitting any information about which enum member it is and which enum it is from. Given these conflicts of interest, I don't see any dataclass examples making it into the enum documentation. -- ~Ethan~

Ethan Furman wrote: > On 7/7/22 09:01, Steve Jorgensen wrote: > > Actually, maybe these are fundamentally incompatible? > > Their intended use seems fundamentally incompatible: > - dataclass was designed for making many mutable records (hundreds, thousands, or more) > - enum was designed to make a handful of named constants (I haven't yet seen one with even a hundred elements) > The repr from a combined dataclass/enum looks like a dataclass, giving no clue that the object is an enum, and omitting > any information about which enum member it is and which enum it is from. > Given these conflicts of interest, I don't see any dataclass examples making it into the enum documentation. > -- > ~Ethan~ Per my subsequent self-reply, they are only incompatible when trying to do them at the same time in the same class definition. It works great to combine them by defining the dataclass as a mixin for the Enum class. Why would it not be good to include that as an example in the official docs, assuming (as I believe) that it is a particularly useful combination?

I don't think that dataclasses have the limited set of intended uses that you are interpreting them as having. To me, the fact that they can be frozen makes them a good fit with Enum.

On 9 Jul 2022, at 22:53, Steve Jorgensen <stevecjor@gmail.com> wrote:
I don't think that dataclasses have the limited set of intended uses that you are interpreting them as having. To me, the fact that they can be frozen makes them a good fit with Enum.
Please quote the email that you are replying to. It is usually considered a code smell to have a class that is two or more things. This seems to be what you are trying to do. How can one class be a set of fields and also the enum for one of its own fields? I do not understand why this is resonable. Barry

I've seen this thread, and also wondered why anyone could EVER want a dataclass that is an enum. Nothing I've seen in the thread gives me any hint about that, really. On Sun, Jul 10, 2022 at 7:44 AM Barry Scott <barry@barrys-emacs.org> wrote:
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.

David Mertz, Ph.D. wrote:
Sorry, I don't know how I communicated that I was trying to have one class be a set of fields and also the enum for one of its own fields. I'm really just wanting to have each member of the enum be an instance of a frozen dataclass. If an of the dataclass fields were of an enum type, then it would presumably not be for the same enum. In my example, none of the fields of the dataclass contains an enum. One contains a string, and the other contains an int.

On Tue, 12 Jul 2022, 18:05 Steve Jorgensen, <stevecjor@gmail.com> wrote:
The ability to unpack a namedtuple as an iterable is considered to be a great advantage the dataclass has over the named tuple because changing the number of members is a backwards incompatible change for namedtuple. No reason in principle why a frozen dataclass should be less memory efficient than a namedtuple (?). Michael _______________________________________________

On 7/6/22 17:01, Steve Jorgensen wrote:
So why use dataclass then? class Creature(Enum): Beetle = (6, 'small') Dog = (4, 'medium') def __init__(self, legs, size): self.legs = legs self.size = size and
list(Creature) [<Creature.Beetle: (6, 'small')>, <Creature.Dog: (4, 'medium')>]
Creature.Beetle.size 'small'
Creature.Beetle.legs 6
It looks like dataclass was just making you do a bunch of extra work. -- ~Ethan~

After some playing around, I figured out a pattern that works without any changes to the implementations of `dataclass` or `Enum`, and I like this because it keeps the 2 kinds of concern separate. Maybe I'll try submitting an MR to add an example like this to the documentation for `Enum`. In [1]: from dataclasses import dataclass In [2]: from enum import Enum In [3]: @dataclass(frozen=True) ...: class CreatureDataMixin: ...: size: str ...: legs: int ...: In [4]: class Creature(CreatureDataMixin, Enum): ...: BEETLE = ('small', 6) ...: DOG = ('medium', 4) ...: In [5]: Creature.DOG Out[5]: Creature(size='medium', legs=4)

On Fri, 8 Jul 2022 at 02:22, Steve Jorgensen <stevecjor@gmail.com> wrote:
I really like this example. I love dataclasses and I love enums and it looks like they go together like peanut butter and chocolate.You get a free initialiser (__init__ method) and all the other goodness of dataclasses (which are really very good). I tweeted your example and it got 29 likes :-) https://twitter.com/voidspace/status/1546832332056924161 Michael
-- Michael Foord Python Consultant, Contractor and Trainer https://agileabstractions.com/

On 7/7/22 18:22, Steve Jorgensen wrote:
I'm impressed that you found a way to make it work. Be aware that some of the bug-fixing in 3.11 has changed the resulting repr() -- the above now looks like: <Creature.DOG: CreatureDataMixin(size='medium', legs=4)> It would be possible to have Enum check to see if the data type is a dataclass, and then see if the repr is set to be automatically created:
CreatureDataMixin.__dataclass_params__ _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=True)
I'll have to look into that. It does seem like a lot of extra work, or at least no less work, than just writing an `__init__` in the enum class directly. -- ~Ethan~

On 7/7/22 09:01, Steve Jorgensen wrote:
Actually, maybe these are fundamentally incompatible?
Their intended use seems fundamentally incompatible: - dataclass was designed for making many mutable records (hundreds, thousands, or more) - enum was designed to make a handful of named constants (I haven't yet seen one with even a hundred elements) The repr from a combined dataclass/enum looks like a dataclass, giving no clue that the object is an enum, and omitting any information about which enum member it is and which enum it is from. Given these conflicts of interest, I don't see any dataclass examples making it into the enum documentation. -- ~Ethan~

Ethan Furman wrote: > On 7/7/22 09:01, Steve Jorgensen wrote: > > Actually, maybe these are fundamentally incompatible? > > Their intended use seems fundamentally incompatible: > - dataclass was designed for making many mutable records (hundreds, thousands, or more) > - enum was designed to make a handful of named constants (I haven't yet seen one with even a hundred elements) > The repr from a combined dataclass/enum looks like a dataclass, giving no clue that the object is an enum, and omitting > any information about which enum member it is and which enum it is from. > Given these conflicts of interest, I don't see any dataclass examples making it into the enum documentation. > -- > ~Ethan~ Per my subsequent self-reply, they are only incompatible when trying to do them at the same time in the same class definition. It works great to combine them by defining the dataclass as a mixin for the Enum class. Why would it not be good to include that as an example in the official docs, assuming (as I believe) that it is a particularly useful combination?

I don't think that dataclasses have the limited set of intended uses that you are interpreting them as having. To me, the fact that they can be frozen makes them a good fit with Enum.

On 9 Jul 2022, at 22:53, Steve Jorgensen <stevecjor@gmail.com> wrote:
I don't think that dataclasses have the limited set of intended uses that you are interpreting them as having. To me, the fact that they can be frozen makes them a good fit with Enum.
Please quote the email that you are replying to. It is usually considered a code smell to have a class that is two or more things. This seems to be what you are trying to do. How can one class be a set of fields and also the enum for one of its own fields? I do not understand why this is resonable. Barry

I've seen this thread, and also wondered why anyone could EVER want a dataclass that is an enum. Nothing I've seen in the thread gives me any hint about that, really. On Sun, Jul 10, 2022 at 7:44 AM Barry Scott <barry@barrys-emacs.org> wrote:
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.

David Mertz, Ph.D. wrote:
Sorry, I don't know how I communicated that I was trying to have one class be a set of fields and also the enum for one of its own fields. I'm really just wanting to have each member of the enum be an instance of a frozen dataclass. If an of the dataclass fields were of an enum type, then it would presumably not be for the same enum. In my example, none of the fields of the dataclass contains an enum. One contains a string, and the other contains an int.

On Tue, 12 Jul 2022, 18:05 Steve Jorgensen, <stevecjor@gmail.com> wrote:
The ability to unpack a namedtuple as an iterable is considered to be a great advantage the dataclass has over the named tuple because changing the number of members is a backwards incompatible change for namedtuple. No reason in principle why a frozen dataclass should be less memory efficient than a namedtuple (?). Michael _______________________________________________

On 7/6/22 17:01, Steve Jorgensen wrote:
So why use dataclass then? class Creature(Enum): Beetle = (6, 'small') Dog = (4, 'medium') def __init__(self, legs, size): self.legs = legs self.size = size and
list(Creature) [<Creature.Beetle: (6, 'small')>, <Creature.Dog: (4, 'medium')>]
Creature.Beetle.size 'small'
Creature.Beetle.legs 6
It looks like dataclass was just making you do a bunch of extra work. -- ~Ethan~
participants (6)
-
Barry Scott
-
Chris Angelico
-
David Mertz, Ph.D.
-
Ethan Furman
-
Michael Foord
-
Steve Jorgensen