I’m still fairly new to Python and I’m trying to get used to its dynamic typing. Sometimes I have a function or a class that expects a parameter of a certain type, but could get a value of another type that’s coercible to it. For example, it might expect a float but instead receive an int or a decimal. Or it might expect a string, but instead receive an object that defines the __str__ special method.
What is the best practice for coercing the argument to the right type (and the reason for it)? Do I do it in the function/class or in the caller? If in the caller, do I also check for it in the function? Eg.
Alternative 1:
def myfunc(takes_float):
myval = float(takes_float)
myfunc(5)
Alternative 2:
def myfunc(takes_float):
myval = takes_float
myfunc(float(5))
Alternative 3:
def myfunc(takes_float):
assert isinstance(takes_float, float)
myval = takes_float
myfunc(float(5))
I’ve already read this answer and this one and they say that checking types in Python is “bad”, but I don’t want to waste time tracking down very simple bugs which would be instantly picked up by the compiler in a statically typed language.
You “coerce” (perhaps — it could be a noop) when it’s indispensable for you to do so, and no earlier. For example, say you have a function that takes a float and returns the sum of its sine and cosine:
Where should you “coerce” x to float? Answer: nowhere at all — sin and cos do that job for you, e.g.:
So when it is indispensable to coerce (as late as possible)? For example, if you want to call string methods on an argument, you do have to make sure it’s a string — trying to call e.g.
.loweron a non-string won’t work,lenmight work but do something different than you expect if the arg is e.g. a list (give you the number of items in the list, not the number of characters its representation as a string will take up), and so forth.As for catching errors — think unit tests — semidecent unit tests will catch all errors static typing would, and then some. But, that’s a different subject.