Is it good code style if a Python function returns different types depending on the arguments provided?
def foo(bar):
if bar is None:
return None
elif bar == 1:
return 1*1
else:
return [b*b for b in bar]
foo
returns None
if bar is None
foo
return 1
if bar == 1
foo
returns a List of int
if bar is a Tuple / List of integers
Examples:
>> foo(None)
None
>> foo(1)
1
>> foo(1, 2, 3, 4)
[1, 4, 9, 16]
Returning None
or an int
should be OK, but is it OK to return an int
or a list of int
s depending on the function arguments? You could argue that it would be OK because the user knows which return types to expect and doesn't need type checking (in which case I would say it's not OK), but one could argue that it would be better to split the function into two functions, one expecting a int
and return an int
and one expecting a list of int
and returning a list of int
s.
Everything in Python is an object. So, your functions can return numeric values ( int , float , and complex values), collections and sequences of objects ( list , tuple , dictionary , or set objects), user-defined objects, classes, functions, and even modules or packages.
In Python, you can return multiple values by simply return them separated by commas. In Python, comma-separated values are considered tuples without parentheses, except where required by syntax. For this reason, the function in the above example returns a tuple with each value as an element.
As you see, default arguments are not required in the function call, and the value of language will always default to Python if another value isn't provided in the call. There can be more than one default value passed to the function.
We can pass multiple arguments to a python function by predetermining the formal parameters in the function definition.
It depends entirely on your use case. Here's an example in the standard library where the type of the result depends on the type of the input:
>>> import operator
>>> operator.add(1, 2)
3
>>> operator.add(1.0, 2.0)
3.0
This type of behavior is usually ok, and can be documented with @typing.overload
.
Here's an example where the type of the result depends on the value of the input:
>>> import json
>>> json.loads('1')
1
>>> json.loads('[1]')
[1]
This type of behavior is usually reserved for serialization and deserialization, or APIs which blur the type / value boundary such as astype
in np.int_(3).astype(bool)
.
On the other hand, here's an example of a function which is obviously poorly designed:
from typing import Union # make sure to document the mixed return type
def is_even(x: int) -> Union[bool, str]:
if x % 2 == 0:
return True
else:
return "no"
Without knowing your specific use case, it's hard to give advice here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With