First that comes to mind is matplotlib. Why are parameters strings? E.g. fig.legend(loc='topleft').
Wouldn't it be much more elegant for enum LegendPlacement.TOPLEFT to exist?
What was their reasoning when they decided "it'll be strings"?
EDIT: So many great answers already! Much to learn from this...
Literal of enum values
Amend PEP 586 to make `enum` values subtypes of `Literal` - Typing - Discussions on Python.org
Python, what's the Enum type good for? - Stack Overflow
Using Enumerated Types in Python
Videos
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.
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
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?
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"]