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=<sentinel>`, 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.


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

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=<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?

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=<sentinel>` 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.

Python-ideas mailing list --
To unsubscribe send an email to
Message archived at
Code of Conduct:

--Guido van Rossum (
Python-ideas mailing list --
To unsubscribe send an email to
--Guido (mobile)