Search code examples
pythonexceptionintegertypechecking

Concise whole float checks


I want to improve my coding.

Recently I was thinking through some code I'd written as part of a constructor in Python. The constructor only allows integers, or whole number floats. I came up with a few ways to check this, but wanted to put it out here to see if there is perhaps a more concise way to do this double exception check.

Method 1: The try, except.

try:
  if not float(x).is_integer:
    raise ValueError('X must be a whole number float')
except:
  raise ValueError('X must be an int or a float'):

Method 2: The double if.

if not isinstance(x, (int, float)):
    raise ValueError('X must be an int or whole number float.')

elif not float(x).is_integer():
    raise ValueError('X must be a whole number float.')

Method 3: Disallow any non-integer.

if not isinstance(x, int):
    raise ValueError('X must be an int.')

I want to see what the best way to do this would be. Method 3 will save some lines of code, although it adds unnecessary restrictions on the code. Methods 1 and 2 are more flexible and reflective of the intent of the checks- to provide flexibility and transparency into why the inputs are not accepted.

Is there a more concise way to perform the above check?

Thanks in advance!


Solution

  • There is a more compact way to check. First think about what it is you want to check. You care about two things: integer or whole number float. Luckily Python conditionals are short circuit evaluations for or and and meaning they don't need to evaluate the entire condition if one of the conditions "short circuits" the rest.

    You don't need to worry about surrounding things in a try-except if you first check that it's an instance of either an int or float:

    In [37]: class T:
        ...:     def __init__(self, val):
        ...:         if not (isinstance(val, (int, float)) and float(val).is_integer()):
        ...:             raise Exception(f'{val} must be a whole number float or integer')
        ...:
    
    In [38]: T(12)
    Out[38]: <__main__.T at 0x2605d2174f0>
    
    In [39]: T(14.)
    Out[39]: <__main__.T at 0x2605d1dda60>
    
    In [40]: T('hello')  # Raises exception
    

    Because the isinstance is first, if that doesn't pass because I pass in a string, it will never try to convert it to a float.