[Python-ideas] Re: Enum: determining if a value is valid

2021-03-17 Thread Ricky Teachey
On Wed, Mar 17, 2021, 5:34 AM Serhiy Storchaka  wrote:

> 12.03.21 23:48, Ethan Furman пише:
> > A question that comes up quite a bit on Stackoverflow is how to test to
> > see if a value will result in an Enum member, preferably without having
> > to go through the whole try/except machinery.
> >
> ...
> > Thoughts?
>
> The Enum class is already too overloaded. I sometimes think about adding
> SimpleEnum class with minimal simple functionality which would allow to
> use enums in more modules sensitive to import time.
>
> As for solving your problem, try/except looks the best solution to me.
>
> try:
> Color(1)
> except ValueError:
> ... # invalid color
> else:
> ... # valid color
>
> If you don't like try/except, the second best solution is to add a
> module level helper in the enum module:
>
> def find_by_value(cls, value, default=None):
> try:
> return cls(value)
> except ValueError:
> return default
>
> You can add also find_all_by_value(), get_aliases(), etc. It is
> important that they are module-level function, so they do not spoil the
> namespace of the Enum class.
>


+1 on a module level helper.

The preponderance of stackoverflow questions seem convincing enough that
people want to do this and it seems like a reasonable request. And I have
wanted to do something like this myself, so I'd probably use it. I see it
as analogous to situations when you want to use dict.get(key) instead of
dict[key].

But adding a non-dunder method to the Enum class namespace seems more
suboptimal to me compared to a module level helper, because of the
namespace spoiling/crowding issue. No matter what method name were to be
chosen, someone at some point would want to use it as an Enum member name
(unless of course it's a reserved dunder method).
___
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/5G7OWMOQT4QUJIDBTRZKBI7PZWEZZNBJ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-17 Thread Serhiy Storchaka
12.03.21 23:48, Ethan Furman пише:
> A question that comes up quite a bit on Stackoverflow is how to test to
> see if a value will result in an Enum member, preferably without having
> to go through the whole try/except machinery.
> 
> A couple versions ago one could use a containment check:
> 
>   if 1 in Color:
> 
> but than was removed as Enums are considered containers of members, not
> containers of the member values.  It was also possible to define one's
> own `_missing_` method and have it return None or the value passed in,
> but that has also been locked down to either return a member or raise an
> exception.
> 
> At this point I see three options:
> 
> 1) add a `get(value, default=None)` to EnumMeta (similar to `dict.get()`
> 
> 2) add a recipe to the docs
> 
> 3) do nothing
> 
> Thoughts?

The Enum class is already too overloaded. I sometimes think about adding
SimpleEnum class with minimal simple functionality which would allow to
use enums in more modules sensitive to import time.

As for solving your problem, try/except looks the best solution to me.

try:
Color(1)
except ValueError:
... # invalid color
else:
... # valid color

If you don't like try/except, the second best solution is to add a
module level helper in the enum module:

def find_by_value(cls, value, default=None):
try:
return cls(value)
except ValueError:
return default

You can add also find_all_by_value(), get_aliases(), etc. It is
important that they are module-level function, so they do not spoil the
namespace of the Enum class.

___
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/PLCJCLTU5FZDHVMJW3BN7FQVD6BPNJBY/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-16 Thread Marco Sulla
On Mon, 15 Mar 2021 at 20:49, Ethan Furman  wrote:
> Everything considered, I think I like allowing `__contains__` to verify both 
> names and values

What about Enum.values()?

> adding `default=` to the constructor for the value-based "gimme an 
> Enum or None" case

What's the use case, apart checking if the value is a "member" of the enum?
___
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/QMCYEAAZ3UELOL5G3T2T3G72KVKNAJV3/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-16 Thread Ethan Furman

On 3/16/21 11:43 AM, Matt Wozniski wrote:

On Tue, Mar 16, 2021, 2:39 PM Marco Sulla wrote:

On Tue, 16 Mar 2021 at 05:38, Matt Wozniski wrote:



Color.from_value(1)  # returns Color.RED


What if I have an alias?


Aliases are different names for a single Enum member, so a by-value search is 
unaffected by them.


That's a problem with any attempt to find an enum member by value,
since values aren't guaranteed to be unique. With either proposal,
we'd just need to pick one - probably the one that appears first
in the class dict.


This is incorrect.  Enum values are unique -- there is only one member that 
will be returned for any given value.  Aliases are additional names for that 
one member:

from enum import Enum

class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
REDD = 1# to support a silly misspelling

>>> Color.RED


>>> Color(1)


>>> Color['RED']


>>> Color['REDD']


Notice that 'REDD' returns the RED member.  There is no member named REDD in 
Color.

--
~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/XBGO35VFGC7CQLIZJTKH6EAXEKCJJQRC/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-16 Thread Matt Wozniski
That's a problem with any attempt to find an enum member by value, since
values aren't guaranteed to be unique. With either proposal, we'd just need
to pick one - probably the one that appears first in the class dict.

On Tue, Mar 16, 2021, 2:39 PM Marco Sulla 
wrote:

> On Tue, 16 Mar 2021 at 05:38, Matt Wozniski  wrote:
> > Color.from_value(1)  # returns Color.RED
>
> What if I have an alias?
> ___
> 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/NQ535PUFCWRBBN5QVTGB7QOBNJNJJEPO/
> 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/B7PEYLOVWD7KOI4SUUXLU224N6XAJ2JR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-16 Thread Marco Sulla
On Tue, 16 Mar 2021 at 05:38, Matt Wozniski  wrote:
> Color.from_value(1)  # returns Color.RED

What if I have an alias?
___
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/NQ535PUFCWRBBN5QVTGB7QOBNJNJJEPO/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-15 Thread Guido van Rossum
You have a good point (and as static typing proponent I should have thought
of that).

Maybe there is not actually a use case for passing an arbitrary default?
Then maybe overloading __contains__ (‘in’) might be better? The ergonomics
of that seem better for the dominant use case (“is this a valid value for
that enum?”).

On Mon, Mar 15, 2021 at 21:37 Matt Wozniski  wrote:

> I find the idea of having the constructor potentially return something
> other than an instance of the class to be very... off-putting. Maybe it's
> the best option, but my first impression of it isn't favorable, and I can't
> think of any similar case that exists in the stdlib today off the top of my
> head. It seems like we should be able to do better.
>
> If I might propose an alternative before this gets set in stone: what if
> `Enum` provided classmethods `from_value` and `from_name`, each with a
> `default=`, so that you could do:
>
> Color.from_value(1)  # returns Color.RED
> Color.from_value(-1)  # raises ValueError
> Color.from_value(-1, None)  # returns None
>
> Color.from_name("RED")  # returns Color.RED
> Color.from_name("BLURPLE")  # raises ValueError
> Color.from_name("BLURPLE", None)  # returns None
>
> That still allows each concept to be expressed in a single line, and
> remains explicit about whether the lookup is happening by name or by value.
> It allows spelling `default=None` as just `None`, as we desire. And instead
> of being a `__contains__` with unusual semantics coupled with a constructor
> with unusual semantics, it's a pair of class methods that each have fairly
> unsurprising semantics.
>
> ~Matt
>
> On Mon, Mar 15, 2021 at 3:55 PM Guido van Rossum  wrote:
>
>> +1
>>
>> On Mon, Mar 15, 2021 at 12:48 PM Ethan Furman  wrote:
>>
>>> On 3/15/21 11:27 AM, Guido van Rossum wrote:
>>> > On Mon, Mar 15, 2021 at 10:53 AM Ethan Furman wrote:
>>>
>>> >> Part of the reason is that there are really two ways to identify an
>>> >> enum -- by name, and by value -- which should `__contains__` work
>>> with?
>>> >
>>> > The two sets don't overlap, so we could allow both. (Funny
>>> > interpretations of `__contains__` are not unusual, e.g.
>>> > substring checks are spelled 'abc' in 'fooabcbar'.)
>>>
>>> They could overlap if the Enum is a `str`-subclass -- although having
>>> the name of one member match the value of a different member seems odd.
>>>
>>> >> I think I like your constructor change idea, with a small twist:
>>> >>
>>> >>   Color(value=, name=, default=)
>>> >>
>>> >> This would make it possible to search for an enum by value or by name,
>>> >> and also specify a default return value (raising an exception if the
>>> >> default is not set and a member cannot be found).
>>> >
>>> >
>>> > So specifically this would allow (hope my shorthand is clear):
>>> > ```
>>> > Color['RED'] --> Color.RED or raises
>>> > Color(1) -> Color.RED or raises
>>> > Color(1, default=None) -> Color.RED or None
>>> > Color(name='RED', default=None) -> Color.RED or None
>>> > ```
>>> > This seems superficially reasonable. I'm not sure what
>>> > Color(value=1, name='RED') would do -- insist that both value and
>>> > name match? Would that have a use case?
>>>
>>> I would enforce that both match, or raise.  Also not sure what the
>>> use-case would be.
>>>
>>> > My remaining concern is that it's fairly verbose -- assuming we don't
>>> > really need the name argument, it would be attractive if we could
>>> > write Color(1, None) instead of Color(1, default=None).
>>> >
>>> > Note that instead of Color(name='RED') we can already write this:
>>> > ```
>>> > getattr(Color, 'RED') -> Color.RED or raises
>>> > getattr(Color, 'RED', None) -> Color.RED or None
>>>
>>> Very good points.
>>>
>>> Everything considered, I think I like allowing `__contains__` to verify
>>> both names and values, adding `default=` to the constructor for
>>> the value-based "gimme an Enum or None" case, and recommending  `getattr`
>>> for the name-based "gimme an Enum or None" case.
>>>
>>> --
>>> ~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/UQBSDZQJWBKMOVSUES7HEDJTYR76Y5N2/
>>> Code of Conduct: http://python.org/psf/codeofconduct/
>>>
>>
>>
>> --
>> --Guido van Rossum (python.org/~guido)
>> *Pronouns: he/him **(why is my pronoun here?)*
>> 
>> ___
>> 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
>> 

[Python-ideas] Re: Enum: determining if a value is valid

2021-03-15 Thread Matt Wozniski
I find the idea of having the constructor potentially return something
other than an instance of the class to be very... off-putting. Maybe it's
the best option, but my first impression of it isn't favorable, and I can't
think of any similar case that exists in the stdlib today off the top of my
head. It seems like we should be able to do better.

If I might propose an alternative before this gets set in stone: what if
`Enum` provided classmethods `from_value` and `from_name`, each with a
`default=`, so that you could do:

Color.from_value(1)  # returns Color.RED
Color.from_value(-1)  # raises ValueError
Color.from_value(-1, None)  # returns None

Color.from_name("RED")  # returns Color.RED
Color.from_name("BLURPLE")  # raises ValueError
Color.from_name("BLURPLE", None)  # returns None

That still allows each concept to be expressed in a single line, and
remains explicit about whether the lookup is happening by name or by value.
It allows spelling `default=None` as just `None`, as we desire. And instead
of being a `__contains__` with unusual semantics coupled with a constructor
with unusual semantics, it's a pair of class methods that each have fairly
unsurprising semantics.

~Matt

On Mon, Mar 15, 2021 at 3:55 PM Guido van Rossum  wrote:

> +1
>
> On Mon, Mar 15, 2021 at 12:48 PM Ethan Furman  wrote:
>
>> On 3/15/21 11:27 AM, Guido van Rossum wrote:
>> > On Mon, Mar 15, 2021 at 10:53 AM Ethan Furman wrote:
>>
>> >> Part of the reason is that there are really two ways to identify an
>> >> enum -- by name, and by value -- which should `__contains__` work with?
>> >
>> > The two sets don't overlap, so we could allow both. (Funny
>> > interpretations of `__contains__` are not unusual, e.g.
>> > substring checks are spelled 'abc' in 'fooabcbar'.)
>>
>> They could overlap if the Enum is a `str`-subclass -- although having the
>> name of one member match the value of a different member seems odd.
>>
>> >> I think I like your constructor change idea, with a small twist:
>> >>
>> >>   Color(value=, name=, default=)
>> >>
>> >> This would make it possible to search for an enum by value or by name,
>> >> and also specify a default return value (raising an exception if the
>> >> default is not set and a member cannot be found).
>> >
>> >
>> > So specifically this would allow (hope my shorthand is clear):
>> > ```
>> > Color['RED'] --> Color.RED or raises
>> > Color(1) -> Color.RED or raises
>> > Color(1, default=None) -> Color.RED or None
>> > Color(name='RED', default=None) -> Color.RED or None
>> > ```
>> > This seems superficially reasonable. I'm not sure what
>> > Color(value=1, name='RED') would do -- insist that both value and
>> > name match? Would that have a use case?
>>
>> I would enforce that both match, or raise.  Also not sure what the
>> use-case would be.
>>
>> > My remaining concern is that it's fairly verbose -- assuming we don't
>> > really need the name argument, it would be attractive if we could
>> > write Color(1, None) instead of Color(1, default=None).
>> >
>> > Note that instead of Color(name='RED') we can already write this:
>> > ```
>> > getattr(Color, 'RED') -> Color.RED or raises
>> > getattr(Color, 'RED', None) -> Color.RED or None
>>
>> Very good points.
>>
>> Everything considered, I think I like allowing `__contains__` to verify
>> both names and values, adding `default=` to the constructor for
>> the value-based "gimme an Enum or None" case, and recommending  `getattr`
>> for the name-based "gimme an Enum or None" case.
>>
>> --
>> ~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/UQBSDZQJWBKMOVSUES7HEDJTYR76Y5N2/
>> Code of Conduct: http://python.org/psf/codeofconduct/
>>
>
>
> --
> --Guido van Rossum (python.org/~guido)
> *Pronouns: he/him **(why is my pronoun here?)*
> 
> ___
> 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/ZK7KKABFNSFC4UY763262O2VIPZ5YDPQ/
> 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/LJ3UQT3JDKFO4F2YBEPL6DFLPADQ4ESR/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-15 Thread Guido van Rossum
+1

On Mon, Mar 15, 2021 at 12:48 PM Ethan Furman  wrote:

> On 3/15/21 11:27 AM, Guido van Rossum wrote:
> > On Mon, Mar 15, 2021 at 10:53 AM Ethan Furman wrote:
>
> >> Part of the reason is that there are really two ways to identify an
> >> enum -- by name, and by value -- which should `__contains__` work with?
> >
> > The two sets don't overlap, so we could allow both. (Funny
> > interpretations of `__contains__` are not unusual, e.g.
> > substring checks are spelled 'abc' in 'fooabcbar'.)
>
> They could overlap if the Enum is a `str`-subclass -- although having the
> name of one member match the value of a different member seems odd.
>
> >> I think I like your constructor change idea, with a small twist:
> >>
> >>   Color(value=, name=, default=)
> >>
> >> This would make it possible to search for an enum by value or by name,
> >> and also specify a default return value (raising an exception if the
> >> default is not set and a member cannot be found).
> >
> >
> > So specifically this would allow (hope my shorthand is clear):
> > ```
> > Color['RED'] --> Color.RED or raises
> > Color(1) -> Color.RED or raises
> > Color(1, default=None) -> Color.RED or None
> > Color(name='RED', default=None) -> Color.RED or None
> > ```
> > This seems superficially reasonable. I'm not sure what
> > Color(value=1, name='RED') would do -- insist that both value and
> > name match? Would that have a use case?
>
> I would enforce that both match, or raise.  Also not sure what the
> use-case would be.
>
> > My remaining concern is that it's fairly verbose -- assuming we don't
> > really need the name argument, it would be attractive if we could
> > write Color(1, None) instead of Color(1, default=None).
> >
> > Note that instead of Color(name='RED') we can already write this:
> > ```
> > getattr(Color, 'RED') -> Color.RED or raises
> > getattr(Color, 'RED', None) -> Color.RED or None
>
> Very good points.
>
> Everything considered, I think I like allowing `__contains__` to verify
> both names and values, adding `default=` to the constructor for
> the value-based "gimme an Enum or None" case, and recommending  `getattr`
> for the name-based "gimme an Enum or None" case.
>
> --
> ~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/UQBSDZQJWBKMOVSUES7HEDJTYR76Y5N2/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
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/ZK7KKABFNSFC4UY763262O2VIPZ5YDPQ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-15 Thread Ethan Furman

On 3/15/21 11:27 AM, Guido van Rossum wrote:

On Mon, Mar 15, 2021 at 10:53 AM Ethan Furman wrote:



Part of the reason is that there are really two ways to identify an
enum -- by name, and by value -- which should `__contains__` work with?


The two sets don't overlap, so we could allow both. (Funny
interpretations of `__contains__` are not unusual, e.g.
substring checks are spelled 'abc' in 'fooabcbar'.)


They could overlap if the Enum is a `str`-subclass -- although having the name 
of one member match the value of a different member seems odd.


I think I like your constructor change idea, with a small twist:

  Color(value=, name=, default=)

This would make it possible to search for an enum by value or by name,
and also specify a default return value (raising an exception if the
default is not set and a member cannot be found).



So specifically this would allow (hope my shorthand is clear):
```
Color['RED'] --> Color.RED or raises
Color(1) -> Color.RED or raises
Color(1, default=None) -> Color.RED or None
Color(name='RED', default=None) -> Color.RED or None
```
This seems superficially reasonable. I'm not sure what
Color(value=1, name='RED') would do -- insist that both value and
name match? Would that have a use case?


I would enforce that both match, or raise.  Also not sure what the use-case 
would be.


My remaining concern is that it's fairly verbose -- assuming we don't
really need the name argument, it would be attractive if we could
write Color(1, None) instead of Color(1, default=None).

Note that instead of Color(name='RED') we can already write this:
```
getattr(Color, 'RED') -> Color.RED or raises
getattr(Color, 'RED', None) -> Color.RED or None


Very good points.

Everything considered, I think I like allowing `__contains__` to verify both names and values, adding 
`default=` to the constructor for the value-based "gimme an Enum or None" case, 
and recommending  `getattr` for the name-based "gimme an Enum or None" case.

--
~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/UQBSDZQJWBKMOVSUES7HEDJTYR76Y5N2/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-15 Thread Ricky Teachey
On Mon, Mar 15, 2021 at 2:28 PM Guido van Rossum  wrote:

>
> ...
>>
>> I think I like your constructor change idea, with a small twist:
>>
>>  Color(value=, name=, default=)
>>
>> This would make it possible to search for an enum by value or by name,
>> and also specify a default return value (raising an exception if the
>> default is not set and a member cannot be found).
>>
>
> So specifically this would allow (hope my shorthand is clear):
> ```
> Color['RED'] --> Color.RED or raises
> Color(1) -> Color.RED or raises
> Color(1, default=None) -> Color.RED or None
> Color(name='RED', default=None) -> Color.RED or None
> ```
>

Additional possibility (just raising it; neither for nor against) with PEP
637:

Color['RED', default=None] --> Color.RED or None

---
Ricky.

"I've never met a Kentucky man who wasn't either thinking about going home
or actually going home." - Happy Chandler
___
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/IUSG2AZ6E6VCI7KVC6WQHESQYBJXZLAD/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-15 Thread Guido van Rossum
On Mon, Mar 15, 2021 at 10:53 AM Ethan Furman  wrote:

> On 3/12/21 5:28 PM, Guido van Rossum wrote:
> > On Fri, Mar 12, 2021 at 1:52 PM Ethan Furman wrote:
>
> >> A question that comes up quite a bit on Stackoverflow is how to test
> >> to see if a value will result in an Enum member, preferably without
> >> having to go through the whole try/except machinery.
> >>
> >> A couple versions ago one could use a containment check:
> >>
> >> if 1 in Color:
> >>
> >> but than was removed as Enums are considered containers of members,
> >> not containers of the member values.
> >
> > Maybe you were a bit too quick in deleting it. Was there a serious
> > bug that led to the removal? Could it be restored?
>
> Part of the reason is that there are really two ways to identify an
> enum -- by name, and by value -- which should `__contains__` work with?
>

The two sets don't overlap, so we could allow both. (Funny interpretations
of `__contains__` are not unusual, e.g. substring checks are spelled 'abc'
in 'fooabcbar'.)


> >> At this point I see three options:
> >>
> >> 1) add a `get(value, default=None)` to EnumMeta (similar to
> `dict.get()`
> >
> > But the way to convert a raw value to an enum value is Color(1), not
> > Color[1], so Color.get(1) seems inconsistent.
>
> Very good point.
>
> > Maybe you can just change the constructor so you can spell this as
> > Color(1, default=None) (and then check whether that's None)?
>
> An interesting idea.
>
> >> 2) add a recipe to the docs
> >
> > But what would the recipe say? Apparently you're looking for a one-liner,
> > since you reject the try/except solution.
>
> The recipe would be for a method that could be added to an Enum, such as:
>
>  @classmethod
>  def get_by_value(cls, value, default=None):
>  for member in cls:
>  if member.value == value:
>  return member
>  return default
>

But that's a non-solution -- people can figure out how to write such a
helper just fine (although probably using try/except) but they don't want
to -- they have *one* line where they want to do this check and so they're
going for a local solution -- probably the try/except.


> >> 3) do nothing
> >
> > Always a good option. :-)
>
> Yes, but not always a satisfying one.  :)
>
> >  Where's that StackOverflow item? How many upvotes does it have?
>
>
> 93 - How do I test if int value exists in Python Enum without using
> try/catch?
>  https://stackoverflow.com/q/43634618/208880
>
> 25 - How to test if an Enum member with a certain name exists?
>  https://stackoverflow.com/q/29795488/208880
>
>   3 - Validate value is in Python Enum values
>  https://stackoverflow.com/q/54126570/208880
>
>   2 - How to check if string exists in Enum of strings?
>  https://stackoverflow.com/q/63335753/208880
>
> I think I like your constructor change idea, with a small twist:
>
>  Color(value=, name=, default=)
>
> This would make it possible to search for an enum by value or by name, and
> also specify a default return value (raising an exception if the default is
> not set and a member cannot be found).
>

So specifically this would allow (hope my shorthand is clear):
```
Color['RED'] --> Color.RED or raises
Color(1) -> Color.RED or raises
Color(1, default=None) -> Color.RED or None
Color(name='RED', default=None) -> Color.RED or None
```
This seems superficially reasonable. I'm not sure what Color(value=1,
name='RED') would do -- insist that both value and name match? Would that
have a use case?

My remaining concern is that it's fairly verbose -- assuming we don't
really need the name argument, it would be attractive if we could write
Color(1, None) instead of Color(1, default=None).

Note that instead of Color(name='RED') we can already write this:
```
getattr(Color, 'RED') -> Color.RED or raises
getattr(Color, 'RED', None) -> Color.RED or None
```

-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
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/CIXFX5SL5DX4ISZOWXJFFYVQK53FGQ27/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-15 Thread Ethan Furman

On 3/12/21 5:28 PM, Guido van Rossum wrote:

On Fri, Mar 12, 2021 at 1:52 PM Ethan Furman wrote:



A question that comes up quite a bit on Stackoverflow is how to test
to see if a value will result in an Enum member, preferably without
having to go through the whole try/except machinery.

A couple versions ago one could use a containment check:

if 1 in Color:

but than was removed as Enums are considered containers of members,
not containers of the member values.


Maybe you were a bit too quick in deleting it. Was there a serious
bug that led to the removal? Could it be restored?


Part of the reason is that there are really two ways to identify an
enum -- by name, and by value -- which should `__contains__` work with?


At this point I see three options:

1) add a `get(value, default=None)` to EnumMeta (similar to `dict.get()` 


But the way to convert a raw value to an enum value is Color(1), not
Color[1], so Color.get(1) seems inconsistent.


Very good point.


Maybe you can just change the constructor so you can spell this as
Color(1, default=None) (and then check whether that's None)?


An interesting idea.

2) add a recipe to the docs 


But what would the recipe say? Apparently you're looking for a one-liner,
since you reject the try/except solution.


The recipe would be for a method that could be added to an Enum, such as:

@classmethod
def get_by_value(cls, value, default=None):
for member in cls:
if member.value == value:
return member
return default


3) do nothing


Always a good option. :-)


Yes, but not always a satisfying one.  :)


 Where's that StackOverflow item? How many upvotes does it have?



93 - How do I test if int value exists in Python Enum without using try/catch?
https://stackoverflow.com/q/43634618/208880

25 - How to test if an Enum member with a certain name exists?
https://stackoverflow.com/q/29795488/208880

 3 - Validate value is in Python Enum values
https://stackoverflow.com/q/54126570/208880

 2 - How to check if string exists in Enum of strings?
https://stackoverflow.com/q/63335753/208880

I think I like your constructor change idea, with a small twist:

Color(value=, name=, default=)

This would make it possible to search for an enum by value or by name, and also 
specify a default return value (raising an exception if the default is not set 
and a member cannot be found).

--
~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/LJXMAORK2JVYTPQQ332NFEV2JPVFVJAX/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-12 Thread Guido van Rossum
On Fri, Mar 12, 2021 at 1:52 PM Ethan Furman  wrote:

> A question that comes up quite a bit on Stackoverflow is how to test to
> see if a value will result in an Enum member, preferably without having to
> go through the whole try/except machinery.
>
> A couple versions ago one could use a containment check:
>
>if 1 in Color:
>
> but than was removed as Enums are considered containers of members, not
> containers of the member values.


Maybe you were a bit too quick in deleting it. Was there a serious bug that
led to the removal? Could it be restored?


> It was also possible to define one's own `_missing_` method and have it
> return None or the value passed in, but that has also been locked down to
> either return a member or raise an exception.
>
> At this point I see three options:
>
> 1) add a `get(value, default=None)` to EnumMeta (similar to `dict.get()`
>

But the way to convert a raw value to an enum value is Color(1), not
Color[1], so Color.get(1) seems inconsistent.

Maybe you can just change the constructor so you can spell this as Color(1,
default=None) (and then check whether that's None)?


> 2) add a recipe to the docs
>

But what would the recipe say? Apparently you're looking for a one-liner,
since you reject the try/except solution.


> 3) do nothing
>

Always a good option. :-) Where's that StackOverflow item? How many upvotes
does it have?

-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
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/QRDMKZBL54GU7A7OSDB5GG2HIAGCDTLE/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-12 Thread Ethan Furman

On 3/12/21 2:49 PM, Ricky Teachey wrote:

On Fri, Mar 12, 2021 at 4:52 PM Ethan Furman wrote:



A question that comes up quite a bit on Stackoverflow is how to test
to see if a value will result in an Enum member, preferably without
having to go through the whole try/except machinery.



Could this be an instance where match-case might become the canonical
solution?

I'm probably getting the syntax wrong, but maybe it would be something like:

match value:
 case MyEnum():
 assert isinstance(value, MyEnum)
 case _:
assert not isinstance(value, MyEnum)


The use case is when you have an unknown value that may or may not convert into 
an Enum member.  So a three-member Enum would look something like:

```python
match value:
case MyEnum():
pass
case 1|2|3:
value = MyEnum(value)
case _:
handle_error_or_use_default()
```

Seven lines of code.  try/except would be something like:

```python
try:
value = MyEnum(value)
except ValueError:
handle_error_or_use_default()
```

vs what I'm envisioning:

```python
value = MyEnum.get(value, some_default)
```

or maybe

```python
value = MyEnum.get(value)
if value is None:
handle_error()
```

--
~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/Z7C36KHOFDDM6REA4PN6GPPYOJ22DPBY/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Enum: determining if a value is valid

2021-03-12 Thread Ricky Teachey
On Fri, Mar 12, 2021 at 4:52 PM Ethan Furman  wrote:

> A question that comes up quite a bit on Stackoverflow is how to test to
> see if a value will result in an Enum member, preferably without having to
> go through the whole try/except machinery.
>
> A couple versions ago one could use a containment check:
>
>if 1 in Color:
>
> but than was removed as Enums are considered containers of members, not
> containers of the member values.  It was also possible to define one's own
> `_missing_` method and have it return None or the value passed in, but that
> has also been locked down to either return a member or raise an exception.
>
> At this point I see three options:
>
> 1) add a `get(value, default=None)` to EnumMeta (similar to `dict.get()`
>
> 2) add a recipe to the docs
>
> 3) do nothing
>
> Thoughts?
>
> --
> ~Ethan~


Could this be an instance where match-case might become the canonical
solution?

I'm probably getting the syntax wrong, but maybe it would be something like:

match value:
case MyEnum():
assert isinstance(value, MyEnum)
case _:
 assert not isinstance(value, MyEnum)


---
Ricky.

"I've never met a Kentucky man who wasn't either thinking about going home
or actually going home." - Happy Chandler
___
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/X3DJPKOEQ6IL5BUUS3Y25SNADM2UHYIM/
Code of Conduct: http://python.org/psf/codeofconduct/