On 12/12/20 7:25 PM, Steven D'Aprano wrote:
> On Sat, Dec 12, 2020 at 06:00:17PM -0800, Ethan Furman wrote:
>> Enum is great! Okay, okay, my opinion might be biased. ;)
>>
>> There is one area where Enum is not great -- for a bunch of unrelated
>> values.
>
> I don't know how to interpret that. Surely *in practice* enums are
> always going to be related in some sense?
I certainly hope so -- that was one of the points in creating Enum.
> I don't expect to create an enum class like this:
>
> class BunchOfRandomStuff(Enum):
> ANIMAL = 'Mustela nivalis (Least Weasel)'
> PRIME = 503
> EULER_MASCHERONI_CONSTANT = 0.5772156649015329
> BEST_PICTURE_1984 = 'Amadeus'
> DISTANCE_MELBOURNE_SYDNEY = (16040497, "rack mount units")
Which is why I said, "Enum is not great for a bunch of unrelated values".
> If I did create such an unusual collection of enums, what is the
> standard Enum lacking that makes it "not great"? It seems to work fine
> to me.
Lots of things work -- calling `__len__` instead of `len()` works, but `__len__` is not the best way to get the length
of an object.
Enums are not great for a bunch of unrelated values because:
- duplicate values would all get aliased to one name
- ordinary values should not be compared using `is`
- standard Enums cannot be seamlessly used as their actual value (example in
other email)
[...]
> I don't understand this. Are you suggesting that NamedValues will have a
> `type` attribute **like Enum**, or **in addition** to what Enum provides
> (value and name)?
To be honest, Enum may have a "type" attribute at this point, I don't remember. NamedValues would definitely have a
"type" attribute whose primary purpose is to make the value attribute work.
As an example, consider sre_constant.MAXREPEAT vs sre_constant.MAX_REPEAT (the only difference is the underscore -- took
me a few moments to figure that out).
The sre_constant._NamedIntConstant class adds a name attribute, and returns
that as the repr().
```
>>> sre_constants.MAXREPEAT
MAXREPEAT
>>> sre_constants.MAX_REPEAT
MAX_REPEAT
```
Not very illuminating. I ended up getting the actual value by calling `int()`
on them.
```
>>> int(sre_constants.MAXREPEAT)
4294967295
>>> int(sre_constants.MAX_REPEAT)
42
```
By adding a "type" attribute, getting something useful becomes a little easier:
```
@property
def value(self):
return self._type_(self)
```
or maybe
```
@property
def value(self):
return self._type_.__repr__(self)
```
>> unlike Enum, duplicates are allowed
>
> Um, do you mean duplicate names? How will that work?
No, duplicate values -- but in an Enum the names given to the duplicate value become aliases to the original name/value,
while duplicates in NamedValue would remain different objects.
>> unlike Enum, new values can be added after class definition
>
> Is there a use-case for this?
Yes.
> If there is such a use-case, could we not just given Enums an API for
> adding new values, rather than invent a whole new Enum-by-another-name?
While NamedValues have a similarities to Enum (.name, .value, human readable
repr()), they are not Enums.
>> unlike Enum, a NamedValue can always be used as-is, even if no data type
>> has been mixed in -- in other words, there is no functional difference
>> between MyIntConstants(int, NamedValue) and MyConstants(NamedValue).
>
> Sorry, I don't get that either. How can Enums not be used "as-is"? What
> does that mean?
It means that you can't do things with the actual value of Color.RED, whether that value is an int, a string, or a
whatever, without going through the value attribute.
> Are you suggesting that NamedValue subclasses will automatically insert
> `int` into their MRO?
No, I'm saying that a NamedValue subclass will have int, or string, or frozenset, or whatever the actual values' types
are, in their mro:
```
def __new__(cls, value, name):
actual_type = type(value)
new_value_type = type(cls.__name__, (cls, type(value)), {})
obj = actual_type.__new__(new_value_type, value)
obj._name_ = name
obj._type_ = actual_type
return obj
```
The subclasses are created on the fly. The production code will cache the new
subclasses so they're only created once.
>> If sre_constants was using a new data type, it should probably be IntEnum
>> instead. But sre_parse is a good candidate for NamedValues:
>>
>>
>> class K(NamedValues):
>> DIGITS = frozenset("0123456789")
> [...snip additional name/value pairs...]
>
>> and in use:
>>
>> >>> K.DIGITS
>> K.DIGITS
>> >>> K.DIGITS.name
>> 'DIGITS'
>> >>> K.DIGITS.value
>> frozenset("0123456789")
>
> Why not just use an Enum?
Why use a Counter instead of defaultdict instead of dict? Because, depending on the task, one is more appropriate than
the others.
--
~Ethan~
_______________________________________________
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/KGG7J3VR7D3YWLKWARHAJYZUXEOUFUMU/
Code of Conduct: http://python.org/psf/codeofconduct/