from typing import Literal
def fn1(a: Literal["apple", "banana", "orange"]):
= frozenset({"apple", "banana", "orange"})
valid_fruits
if a not in valid_fruits:
raise ValueError(f"Invalid value: {a}. Must be one of: {', '.join(valid_fruits)}")
# Rest of the function
return a
15 Function (Validation)
15.1 My Arguments Validation
I would use these balanced approach:
"apple") fn1(
'apple'
try:
"watermelon")
fn1(except ValueError as e:
print(e)
Invalid value: watermelon. Must be one of: orange, apple, banana
15.2 Arguments Validation (Approach)
I’ll help you explore different approaches to validate function arguments when there are only specific allowed values. Let’s examine various methods, starting from simple to more sophisticated approaches.
15.2.1 If-else statements (Basic Approach):
def fn1(a):
if a not in ["apple", "banana", "orange"]:
raise ValueError(f"Invalid value: {a}. Must be one of: apple, banana, orange")
# Rest of the function
return a
15.2.2 Using an Enum class (Type Safety)
from enum import Enum, auto
class Fruit(Enum):
= "apple"
APPLE = "banana"
BANANA = "orange"
ORANGE
def fn1(a: Fruit) -> str:
# The type hint ensures only Fruit enum values are accepted
return a.value
15.2.3 Using a Set for faster lookup:
= frozenset({"apple", "banana", "orange"})
VALID_FRUITS
def fn1(a: str) -> str:
if a not in VALID_FRUITS:
raise ValueError(f"Invalid value: {a}. Must be one of: {', '.join(VALID_FRUITS)}")
return a
15.2.4 Using decorators (Advanced):
from functools import wraps
from typing import Callable, Any
def validate_fruit(func: Callable) -> Callable:
= frozenset({"apple", "banana", "orange"})
valid_fruits
@wraps(func)
def wrapper(a: str, *args: Any, **kwargs: Any) -> Any:
if a not in valid_fruits:
raise ValueError(f"Invalid value: {a}. Must be one of: {', '.join(valid_fruits)}")
return func(a, *args, **kwargs)
return wrapper
@validate_fruit
def fn1(a: str) -> str:
return a
try:
"watermelon")
fn1(except ValueError as e:
print(e)
Invalid value: watermelon. Must be one of: orange, apple, banana
15.2.5 Using Literal type hints (Python 3.8+):
from typing import Literal
def fn1(a: Literal["apple", "banana", "orange"]) -> str:
return a
"watermelon") fn1(
'watermelon'
Let me explain the pros and cons of each approach:
The if-else approach is straightforward and easy to understand, but it’s verbose if you need to reuse the validation in multiple functions.
The Enum approach provides type safety and IDE support, making it harder to make mistakes. It’s especially useful in larger applications where you want to ensure type consistency. However, it requires more setup code.
The Set approach offers faster lookup times compared to lists and is memory efficient. It’s a good balance between simplicity and performance, especially when you have many valid values.
The decorator approach allows you to separate the validation logic from the function implementation, making it reusable across multiple functions. However, it’s more complex and might be overkill for simple cases.
The Literal type hints provide static type checking if you use a type checker like mypy, but they don’t enforce runtime validation by themselves. They’re great for documentation and catching errors during development.
For your specific case, since you mentioned you’re at an intermediate Python level, I would recommend either the Set approach or the Enum approach:
- Use the Set approach if you want something simple and performant
- Use the Enum approach if you want better type safety and IDE support
Would you like me to elaborate on any of these approaches or show you how to combine them for more robust validation?