Pythonic way of using type as function argument
python type annotations
python type() function
python type hint multiple types
python type checking
python function return type
python typing boolean
python function parameter type
What would be the most pythonic way to pass an object type as an agrgument in a function?
Let me give you an example. Let's say I was trying to get a configuration from an environment variable. Because all environment variables are strings I need to cast the value to the correct type.
To do this I need to tell the function the desired type. This is the purpose of the
coerce argument. My first instinct is to pass in the desired type as the value for
coerce. However, I am not sure if there are any implications or problems in doings so.
import os # The Function def get_config(config: str, coerce: type, default: any, delimiter: str = ","): value = os.getenv(config, None) # Get config from environment if value is None: return default # Return default if config is None if coerce is bool: value = str2bool(value) # Cast config to bool elif coerce is int: value = str2int(value) # Cast config to int elif coerce is list: value = value.split(delimiter) # Split string into list on delimiter return value # Return the config value # Usage os.environ["TEST_VAR"] = "True" test_var = get_config("TEST_VAR", bool, False) print(test_var) # output is True print(type(test_var)) # output is <class 'bool'>
To me this seems more clear and pythonic than using a string such as
"bool" to specify the type. However, I would like to know if there could be any problems caused by passing around built in types as function arguments.
You can simplify the code and make it more powerful by just directly passing the conversion function (type annotations are left as an exercise):
def get_config(config, convert, default): value = os.getenv(config, None) return default if value is None else convert(value) test_var = get_config("TEST_VAR", str2bool, False)
and perhaps having a helper function for the list case:
def make_str2list(delimiter=','): return lambda s: s.split(delimiter) test_var = get_config("TEST_VAR", make_str2list(':'), )
How do Python functions handle the types of the parameters that , Python is strongly typed because every object has a type, every object knows its type, it's impossible to accidentally or deliberately use an object of a type "as if" Python type () The type () function either returns the type of the object or returns a new type object based on the arguments passed. The type () function has two different forms: type (object) type (name, bases, dict)
Since all you are doing with the
type argument is to compare it one by one to certain specific types that you're expecting, rather than actually using
type to construct objects of that type, it is doing nothing different from passing in a string such as
'bool' as an argument and compare it to several string constants.
Instead, you can make the conversion functions such as
str2int themselves an argument, so that you can call
coerce(value) to convert
value in a generic way. Store such conversion functions as attributes of a dedicated class for better readability, as demonstrated below:
import os import typing class to_type: bool = 'True'.__eq__ int = int list = lambda s: s.split(',') def get_config(config: str, coerce: typing.Callable = lambda s: s, default: any = None): value = os.getenv(config, None) if value is None: return default return coerce(value) os.environ["TEST_VAR"] = "True" print(get_config("TEST_VAR", to_type.bool)) os.environ["TEST_VAR"] = "2" print(get_config("TEST_VAR", to_type.int)) os.environ["TEST_VAR"] = "a,b,c" print(get_config("TEST_VAR", to_type.list)) os.environ["TEST_VAR"] = "foobar" print(get_config("TEST_VAR"))
True 2 ['a', 'b', 'c'] foobar
typing — Support for type hints, In the function greeting , the argument name is expected to be of type str and the Use object to indicate that a value could be any type in a typesafe manner. Python allows functions to be called using keyword arguments. When we call functions in this way, the order (position) of the arguments can be changed. Following calls to the above function are all valid and produce the same result.
In this specific instance, I'd argue that coerce should not be a type, but rather an Enum: both because
get_config only supports a small set of possible values, and because it doesn't use the type values directly in its handling. If nothing else, the function signature is more precise with an Enum.
ConfigType = Enum('ConfigType', 'STR BOOL INT LIST') def get_config(config: str, coerce: ConfigType, default: any, delimiter: str = ","): value = os.getenv(config, None) # Get config from environment if value is None: return default # Return default if config is None if coerce is ConfigType.BOOL: value = str2bool(value) # Cast config to bool elif coerce is ConfigType.INT: value = str2int(value) # Cast config to int elif coerce is ConfigType.LIST: value = value.split(delimiter) # Split string into list on delimiter return value # Return the config value
That said, if you really wanted to use type then Python 3.8 (which should be released in two days' time) supports literal types, meaning you could declare the function as follows:
def get_config(config: str, coerce: Literal[str, bool, int, list], default: any, delimiter: str = ","):
PEP 612 -- Parameter Specification Variables, There currently are two ways to specify the type of a callable, the function, is an instance of the Python idiom of one function passing all The arbitrary keyword argument dictionary: If your function requires an undetermined series of named arguments, it is possible to use the **kwargs construct. In the function body, kwargs will be a dictionary of all the passed named arguments that have not been caught by other keyword arguments in the function signature. Using *args, Python
Python Type Checking (Guide) – Real Python, Similarly, the optional align argument should have type bool with the default a way to associate arbitrary expressions to function arguments and return values. There is no syntax to indicate optional or keyword arguments; such function types are rarely used as callback types. Callable[, ReturnType] (literal ellipsis) can be used to type hint a callable taking any number of arguments and returning ReturnType .
Defining Your Own Python Function – Real Python, The most straightforward way to pass arguments to a Python function is any way that makes sense for the type of object the function returns. This way, the user of the function can manipulate the recipient list as a list beforehand, and it opens the possibility to pass any sequence, including iterators, that cannot be unpacked as other sequences. The arbitrary keyword argument dictionary is the last way to pass arguments to functions.
Python Function Arguments with Types, Syntax and Examples , In this Python Function Arguments tutorial, we will learn about what function arguments used in Python and its type: Python Keyword Arguments, Default A generic function is composed of multiple functions implementing the same operation for different types. Which implementation should be used during a call is determined by the dispatch algorithm. When the implementation is chosen based on the type of a single argument, this is known as single dispatch.
- this is fine. do you have any specific concerns? This is probably a better fit for CodeReview not StackOverflow
- @juanpa.arrivillaga No specific concern. Just not sure if this will come back to bite me later. Thank you for pointing out CodeReview I did not know it existed!
- yup. plus, you’re already getting some builtins like
int. casting strings via
strdoes nothing, but it generalizes the concept