As other answers have explained, this probably isn't a great design.
First, a "number" could be an int
, or some user-defined subclass of int
, or a float
, or some user-defined Quaternion
type. Normally, you just use duck-typing: it x ** 2
works, then x
is quacking like a number, and that's good enough.
But a list of ints doesn't quack like an int. So, what can you do?
Well, usually, you'll want to explicitly loop over them:
>>> xs = [1, 2, 3]
>>> sqs = [square(x) for x in xs]
… or write a function that does that:
>>> def squares(xs): return [square(x) for x in xs]
>>> sqs = squares(xs)
… or use a type that knows how to vectorize mathematical operators:
>>> xs = np.array([1, 2, 3])
>>> sqs = square(xs)
In fact, even when you do want to handle two different types, you can often rely on duck typing:
def square(x):
try:
return x**2
except ValueError:
return [i**2 for i in x]
This will square anything that's number-like enough to be squarable, and iterate over squaring all of the elements of anything that isn't, and raise a reasonable exception for anything that fails (either because it's not iterable, or because its elements aren't squarable).
Occasionally, you really do need to type-switch. But you still want to keep as close to duck-typing as possible, and that means using isinstance
(so that, e.g., a user subtype of int
still counts as a number) and, usually, using abstract base classes (so that, e.g., a user Quaternion
type still counts as a number).
In this case, that means either treating numbers specially and assuming anything else is an iterable:
def square(x):
if isinstance(x, numbers.Number):
return x**2
else:
return [i**2 for i in x]
… or treating iterables specially and assuming everything else is a number:
def square(x):
if isinstance(x, collections.abc.Iterable):
return [i**2 for i in x]
else:
return x**2
Or maybe treating both specially and calling everything else an error:
def square(x):
if isinstance(x, numbers.Number):
return x**2
elif isinstance(x, collections.abc.Iterable):
return [i**2 for i in x]
raise ValueError(f"'{type(x).__name__}' instance is not a number or numbers")