Reputation: 31
Relatively new to Python, I find myself having to evaluate a lot of arguments in functions/static methods and I was thinking if there is a user-friendly way to do this, so I ended up writing a function like so:
from typing import Any, Optional, Union
# Standard evaluator for default values
def eval_default(x: Any, default: Any, type: Optional[Any] = None) -> Any:
"""Helper function to set default value if None and basic type checking
Args:
x: the value to be evaluated
default: the value to return if x is None
type: the expected type of x, one of bool, int, float, str
Raises:
TypeError: for bool, int, float, str if x is not None and not of type
Returns:
x: either 'as is' if not None, or default if None, unless exception is raised.
"""
# Infer type from default
type = type(default) if type is None else type
# Return default value if x is None, else check type and/or return x
if x is None:
return default
elif not isinstance(x, type):
if type == bool:
raise TypeError("Variable can be either True or False.")
elif type == int:
raise TypeError("Variable must be an integer.")
elif type == float:
raise TypeError("Variable must be a float.")
elif type == str:
raise TypeError("Variable must be a string.")
else:
return x
Then in my main code I can do something like:
def some_method(self, some_variable:Optional[bool] = None) -> bool:
"""Some description
"""
try:
some_variable = eval_default(some_variable, False, bool)
except TypeError:
print("some_variable must be True or False.")
return some_variable
Is there a simpler, more concise or more elegant standard practice to handle such a situation?
Many thanks in advance.
Upvotes: 0
Views: 721
Reputation: 71574
Your eval_default
doesn't do anything to narrow the type from Optional[bool]
to bool
:
def some_method(self, some_variable:Optional[bool] = None) -> bool:
try:
some_variable = eval_default(some_variable, False, bool)
except TypeError:
print("some_variable must be True or False.")
reveal_type(some_variable) # note: Revealed type is "Union[builtins.bool, None]"
It potentially could with careful use of generics, but you'd need to assign the return value to a different name, which only makes it harder to use.
My usual pattern for de-Optional
-ing default-None
args is:
def some_method(self, some_variable:Optional[bool] = None) -> bool:
some_variable = some_variable or False
reveal_type(some_variable) # note: Revealed type is "builtins.bool"
Of course for a bool (or any immutable type) you could just do:
def some_method(self, some_variable: bool = False) -> bool:
reveal_type(some_variable) # note: Revealed type is "builtins.bool"
which avoids the whole problem! The optional_arg or default
trick is really only useful for cases where the default is something you're going to mutate and you need to construct a new object per function call (e.g. you want to initialize the default to a new empty list so you can append items to it).
Upvotes: 1
Reputation: 625
It looks to me like your eval_default
function is just adding unnecessary complexity. If type checking is required, I'd just put it in the function, and declare the default as the actual default:
def some_method(self, some_variable:Optional[bool] = False) -> bool:
"""Some description
"""
if not isinstance(some_variable, bool):
print(f"some_variable must be True of False")
return some_variable
Or if you need a mutable default:
def some_method(self, some_variable:Optional[list] = None) -> bool:
"""Some description
"""
if some_variable is None:
some_variable = []
if not isinstance(some_variable, list):
print(f"some_variable must be a list")
return some_variable
Upvotes: 2