I get the basic philosophy of don't ask for permission, just do it and ask for forgiveness. For instance it doesn't need to be a file to have a .read() method, you'll thank yourself later when you find things "just work" when you want to read a file-like object. However, that's almost easier to get my head around for complicated objects like files than simple objects like numbers.
So I'm trying to write a very simple class. The details don't matter, but it has an internal counter, and has to do something when the counter exceeds a limit. The limit is supplied as an argument when the object is created.
Now I'm thinking defensive programming. Obviously I'd like my limit to be an integer, or at least something that compares like an integer. It can be weaker than that, because the >= test I do will still provide correct behaviour if it's a float.
Although the class user will know that this argument ought to be an integer, I'd like the object to do something unsurprising if it isn't. My first thought was to check that I actually had a number by
if isinstance(limit,(int,long,float)):
but there's any number of "don't test, try except" answers on here, so I know that's unpythonic. It's better to use the limit, and wait for an exception to be thrown. So I tried this inside a try: except for various types of limit
if counter >= limit:
This works fine as expected if limit is an int or a float. I then made limit a string or a tuple, objects for which compare with a number does not have any obvious or reasonable definition, and waited to catch the exception. But it executes, returns a boolean, and stays silent.
So I RTFM, and it turns out that compare does not raise an exception for mixed types, something to do with the operation of "x in container", for which equality is tested on arbitrary types. It does always return not_equal for different types, but magnitude comparison, while consistent, is arbitrary. I could perhaps have used it if a mixed type magnitude compare always returned False, but it will happily return True as well.
If I try to use other integer-like behaviour on non-numerics, I get the expected exception, for instance this string + number throws a TypeError
limit+0
So I could say
if counter >= (limit+0)
and that throws the desired exception if limit is anything other than a number. But that feels almost obfuscated. I'm doing something extra to the expression to do a type test by the back door. Any code optimiser, or another editor, would remove the clearly redundant arithmetic operation. It's like seeing if the object weighs the same as a duck to see if it's made of wood, and so can be burnt as a witch (sort of).
A clearer possibility is
if counter >= int(limit)
which at least has some of the explanation built in. It doesn't do exactly what I'd like to happen under all circumstances, but it does enough for the moment, and it won't surprise anybody.
So WWGD? What's the pythonic way safely to use an untrusted argument, when all you want to do is compare it for magnitude?