I’d guess age and momentum? I don’t think enums were used nearly as much before type hinting was more common. Answer from wyldstallionesquire on reddit.com
🌐
Mypy
mypy.readthedocs.io › en › stable › literal_types.html
Literal types and Enums - mypy 1.20.0 documentation
The examples in this page import ... in Python 3.8, but are also available for use in Python 3.4 - 3.7 via the typing_extensions package. Literal types may contain one or more literal bools, ints, strs, bytes, and enum values....
Discussions

Literal of enum values
I am trying to implement a function that, as the type of one argument, can accept either an Enum or a value of such an enum (because some Python programmers are more used to type strings instead of enum values). Currently I have managed ... More on github.com
🌐 github.com
10
January 21, 2021
Amend PEP 586 to make `enum` values subtypes of `Literal` - Typing - Discussions on Python.org
Inspired by this highly upvoted issue: Literal of enum values · Issue #781 · python/typing · GitHub, I propose that enum values, if they are defined literally and are instances of the type of the literal (So this would be applicable mostly to IntEnum and StrEnum, but not regular Enum) Then ... More on discuss.python.org
🌐 discuss.python.org
11
July 29, 2024
Python, what's the Enum type good for? - Stack Overflow
Appreciate this comprehensive answer greatly. It is seeming to me that one could now replace (entirely?) Enums with Literal types python.org/dev/peps/pep-0586 - thoughts on this? More on stackoverflow.com
🌐 stackoverflow.com
Using Enumerated Types in Python
https://github.com/python/mypy/issues/6366 · https://github.com/python/mypy/issues/5818 More on news.ycombinator.com
🌐 news.ycombinator.com
42
72
June 8, 2020
Top answer
1 of 2
16

I'm afraid there's no such way. The first thing that comes to mind is iterating over enum's values to build a Literal type won't work, because Literal cannot contain arbitrary expressions. So, you cannot specify it explicitly:

# THIS DOES NOT WORK
def is_enabled(state: State | Literal[State.ENABLED.value, State.DISABLED.value]) -> bool:
    ...

There's an open issue on GitHub with the related discussion. Basically, you could have hardcoded literals for every Enum, but in that case, you need to update it in accordance with Enum updates, and one day it will be messy. So, I would either stick with State | str annotation or just State and expect your function to accept only enums.

Also, take into account that you do not need to explicitly create an Enum object to test its value, you can just write "enabled" == State.ENABLED as I mentioned in the comments.

2 of 2
1

It's very annoying that you can't easily create a Literal type hint for all the members of an enum, but one potential workaround if you have control of the enum (and can programmatically determine what the member name should be from the value) is to reverse the direction

Instead of creating a Literal type hint from the enum members, you can create an enum from the values in a Literal type hint using the functional API for Enum and typing.get_args

Your example would look like:

from enum import Enum
from typing import Literal, get_args

# variable naming might not be great here,
# but I figure it's good enough for the short example
StateValues = Literal["enabled", "disabled"]
State = Enum(
    "State",
    ((state.upper(), state) for state in get_args(StateValues))
    module="...",  # fill in module and qualname to help with pickling
    qualname="...",
)

def is_enabled(state: State | StateValues) -> bool:
    return state is State.ENABLED or state == State.ENABLED.value

Huge downside to this approach is that now static analysis tools will flag State.ENABLED as an unknown attribute (and you lose the ability to put individual enum members in type hints or do exhaustiveness checking on enum members)

The functional API won't let you do everything that the class API does, but if all you need are the members to be defined, then this should work


Overall, this approach probably isn't worth it

🌐
GitHub
github.com › python › typing › issues › 781
Literal of enum values · Issue #781 · python/typing
January 21, 2021 - I am trying to implement a function that, as the type of one argument, can accept either an Enum or a value of such an enum (because some Python programmers are more used to type strings instead of enum values). Currently I have managed to do the following: class LinkageCriterion(enum.Enum): WARD = "ward" COMPLETE = "complete" AVERAGE = "average" SINGLE = "single" LinkageCriterionLike = Union[ LinkageCriterion, Literal["ward", "complete", "average", "single"] ]
Author   vnmabus
🌐
Towards Data Science
towardsdatascience.com › home › latest › python type hinting with literal
Python Type Hinting with Literal | Towards Data Science
January 22, 2025 - As of Python 3.9.1, the order of values in a literal type doesn’t matter. Image by author · Consequently, what matters are the possible choices, not their relationships. If utilizing the order of values is essential, consider employing a different type, not a literal one. One solution is to leverage an enumeration type, utilizing the enum.Enum class; we’ll delve into this concept soon, in a dedicated article.
🌐
Python
typing.python.org › en › latest › spec › literal.html
Literals — typing documentation
Literal may be parameterized with literal int, str, bytes, and bool objects, instances of enum.Enum subclasses, and None.
Find elsewhere
🌐
Python.org
discuss.python.org › typing
Amend PEP 586 to make `enum` values subtypes of `Literal` - Typing - Discussions on Python.org
July 29, 2024 - Inspired by this highly upvoted issue: Literal of enum values · Issue #781 · python/typing · GitHub, I propose that enum values, if they are defined literally and are instances of the type of the literal (So this would be applicable mostly to IntEnum and StrEnum, but not regular Enum) Then they should be subtypes of the corresponding literal type.
Top answer
1 of 1
269

What's the purpose of enums? What value do they create for the language? When should I use them and when should I avoid them?

The Enum type got into Python via PEP 435. The reasoning given is:

The properties of an enumeration are useful for defining an immutable, related set of constant values that may or may not have a semantic meaning.

When using numbers and strings for this purpose, they could be characterized as "magic numbers" or "magic strings". Numbers rarely carry with them the semantics, and strings are easily confused (capitalization? spelling? snake or camel-case?)

Days of the week and school letter grades are examples of this kind of collections of values.

Here's an example from the docs:

from enum import Enum

class Color(Enum):
    red = 1
    green = 2
    blue = 3

Like the bare class, this is much more readable and elegant than the namedtuple example, it is also immutable, and it has further benefits as we'll see below.

Strictly dominant: The type of the enum member is the enum

>>> type(Color.red)
<enum 'Color'>
>>> isinstance(Color.green, Color)
True

This allows you to define functionality on the members in the Enum definition. Defining functionality on the values could be accomplished with the other prior methods, but it would be very inelegant.

Improvement: String coercion

The string representation is human readable, while the repr has more information:

>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>

I find this to be an improvement over the magic numbers and even possibly better than strings from the namedtuple.

Iteration (parity):

The enum supports iteration (like the namedtuple, but not so much the bare class) too:

>>> for color in Color:
        print(color)
Color.red
Color.green
Color.blue

The __members__ attribute is an ordered mapping of the names of the enums to their respective enum objects (similar to namedtuple's _asdict() function).

>>> Color.__members__
mappingproxy(OrderedDict([('red', <Color.red: 1>), ('green', <Color.green: 2>), 
('blue', <Color.blue: 3>)]))

Supported by pickle (parity)

You can serialize and deserialize the enum (in case anyone was worried about this):

>>> import pickle
>>> color.red is pickle.loads(pickle.dumps(color.red))
True

Improvement: Aliases

This is a nice feature that the bare class doesn't have, and it would be difficult to tell the alias was there in the namedtuple.

class Color(Enum):
    red = 1
    green = 2
    blue = 3
    really_blue = 3

The alias comes after the canonical name, but they are both the same:

>>> Color.blue is Color.really_blue
True

If aliases should be prohibited to avoid value collisions, use the enum.unique decorator (a strictly dominant feature).

Strictly dominant: comparisons done with is

The enum is intended to be tested with is, which is a fast check for a single object's identity in the process.

>>> Color.red is Color.red
True
>>> Color.red is Color.blue
False
>>> Color.red is not Color.blue
True

Tests for equality work as well, but tests for identity with is are optimal.

Different semantics from other Python classes

Enum classes have different semantics from regular Python types. The values of the Enum are instances of the Enum, and are singletons in memory for those values - there is no other purpose for instantiating them.

>>> Color.red is Color(1)

This is important to keep in mind, perhaps it is a downside, but comparing on this dimension is comparing apples with oranges.

Enums not assumed to be ordered

While the Enum class knows what order the members are created in, enums are not assumed to be ordered. This is a feature because many things that may be enumerated have no natural order, and therefore order would be arbitrary.

However, you can give your enums order (see the next section).

Subclassing

You can't subclass an Enum with members declared, but you can subclass an Enum that doesn't declare members to share behavior (see the OrderedEnum recipe in the docs).

This is a feature - it makes little sense to subclass an Enum with members, but again, the comparison is apples and oranges.

When should I use enum.Enum?

This is the new canonical enumeration in Python. Collaborators will expect your enums to behave like these enums.

Use it anywhere you have a canonical source of enumerated data in your code where you want explicitly specified to use the canonical name, instead of arbitrary data.

For example, if in your code you want users to state that it's not "Green", "green", 2, or "Greene", but Color.green - use the enum.Enum object. It's both explicit and specific.

There are a lot of examples and recipes in the documentation.

When should I avoid them?

Stop rolling your own or letting people guess about magic numbers and strings. Don't avoid them. Embrace them.

However, if your enum members are required to be integers for historic reasons, there's the IntEnum from the same module, which has the same behavior, but is also an integer because it subclasses the builtin int before subclassing Enum. From IntEnum's help:

class IntEnum(builtins.int, Enum)

we can see that the IntEnum values would test as an instance of an int.

🌐
YouTube
youtube.com › watch
python enum vs literal
Enjoy the videos and music you love, upload original content, and share it all with friends, family, and the world on YouTube.
🌐
Python
peps.python.org › pep-0586
PEP 586 – Literal Types - Python Enhancement Proposals
March 14, 2019 - Some of this complexity will be alleviated once Literal types are introduced: rather than entirely special-casing enums, we can instead treat them as being approximately equivalent to the union of their values and take advantage of any existing ...
🌐
Hacker News
news.ycombinator.com › item
Using Enumerated Types in Python | Hacker News
June 8, 2020 - https://github.com/python/mypy/issues/6366 · https://github.com/python/mypy/issues/5818
🌐
GitHub
github.com › sphinx-doc › sphinx › issues › 11473
Enum values inside `typing.Literal` render weirdly · Issue #11473 · sphinx-doc/sphinx
June 25, 2023 - For some reason, it shows ~typing.Literal, rather than just Literal, and the enum variant seems to just be the output of repr(SomeEnum.X), which includes the value of that enum, which isn't something we'd generally need to see.
Author   ItsDrike
🌐
Reddit
reddit.com › r/learnpython › which is better: string, or enum-like global constants in interface?
r/learnpython on Reddit: Which is better: string, or enum-like global constants in interface?
February 3, 2024 -

Contrived example. Suppose you are defining the API of a class FireExtinguisher that takes a constructor argument specifying whether it's type A, B or C. Which interface is better:

import firefighting as ff

extinguisher = ff.FireExtinguisher(ff.C)

# or

extinguisher = ff.FireExtinguisher("C")

or something else?

It's easy to find instances of the first way: for example, the logging module defines logging.DEBUG, logging.INFO, etc. Similarly, the subprocess module defines several constants for Windows. But the string approach looks more concise.

In C, one would argue that the enum style is more efficient and provides a compile time check that you haven't specified an invalid name, while the string version would require a run-time check for validity. But in python, it's a runtime check, anyway, right?

What is the best way to do this in python and why?

🌐
w3tutorials
w3tutorials.net › blog › type-hinting-enum-member-value-in-python
How to Type Hint Enum Member Values in Python Without Literal: Preventing Sync Issues — w3tutorials.net
If you must work with raw values (e.g., for performance or legacy reasons), use TypeVar to create a type restricted to the enum’s value type (e.g., int for Color). While less strict than enum members, this avoids Literal sync issues.
🌐
Python documentation
docs.python.org › 3 › howto › enum.html
Enum HOWTO — Python 3.14.3 documentation
An Enum is a set of symbolic names bound to unique values. They are similar to global variables, but they offer a more useful repr(), grouping, type-safety, and a few other features. They are most ...
🌐
GitHub
github.com › python › mypy › issues › 8657
Enum member aliases does not work correctly with Literal · Issue #8657 · python/mypy
April 11, 2020 - The bigger problem comes, when we want to use Literal on such aliases: import enum from typing import Literal, Optional class Color(enum.Enum): BLACK = enum.auto() BLACK = Color.BLACK BLACK_ALIAS: Literal[Color.BLACK] = Color.BLACK x: Optional[Literal[Color.BLACK]] = None y: Optional[Literal[BLACK]] = None z: Optional[Literal[BLACK_ALIAS]] = None reveal_type(x) reveal_type(y) reveal_type(z)
Author   kprzybyla
🌐
Python.org
discuss.python.org › typing
Inference on Literal Types - Page 2 - Typing - Discussions on Python.org
November 25, 2024 - Here’s another example, contrasting the behavior of mypy and pyright with Enum and Literal. From a typing perspective, my colleagues and I like to view Literal as a typing shorthand for creating an Enum class, but pyright makes this hard to do because of the type “widening” going on. from enum import Enum from typing import Literal class MyEnumAB(Enum): A = 1 B = 2 def enum_returnset(thelist: list[MyEnumAB]) -> set[MyEnumAB]: y = set([x for x in thelist]) reveal_type(y) ...
🌐
Reddit
reddit.com › r/python › thoughts on adding a typing.enumvalues static typing primitive?
r/Python on Reddit: Thoughts on adding a typing.EnumValues static typing primitive?
April 25, 2025 -

I recently had an issue I ran into and had an idea for what I feel would be a really helpful extension to typing, and I wanted to see if anyone else thinks it makes sense.

I was writing a pydantic class with a string field that needs to match one of the values of an Enum.

I could do something like Literal[*[e.value for e in MyEnum]], dynamically unpacking the possible values and putting them into a Literal, but that doesn't work with static type checkers.

Or I could define something separate and static like this:

class MyEnum(str, Enum):
    FIRST = "first"
    SECOND = "second"

type EnumValuesLiteral = Literal["first", "second"]

and use EnumValuesLiteral as my type hint, but then I don't have a single source of truth, and updating one while forgetting to update the other can cause sneaky, unexpected bugs.

This feels like something that could be a pretty common issue - especially in something like an API where you want to easily map strings in requests/responses to Enums in your Python code, I'm wondering if anyone else has come across it/would want something like that?

EDIT: Forgot to outline how this would work ->

from enum import Enum
from typing import EnumValues


class Colors(str, Enum):
    RED = "red"
    BLUE = "blue"
    GREEN = "green"


class Button:
    text: str
    url: str
    color: EnumValues[Colors]  # Equivalent to Literal["red", "blue", "green"]
🌐
Python.org
discuss.python.org › ideas
Atom-like Enums in Python - Ideas - Discussions on Python.org
March 2, 2024 - Currently, to create an Enum, it looks like this: from enum import Enum class Color(Enum): RED = "red" GREEN = "green" BLUE = "blue" And to use an Enum’s value, you’d need to enter Color.RED.value all to return a string literal of "red". It’s a bit cumbersome.