On Mon, Mar 15, 2021 at 10:53 AM Ethan Furman <et...@stoneleaf.us> 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=<sentinel>, name=<sentinel>, default=<sentinal>) > > 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?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
_______________________________________________ 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/