S. Gamgee S. Gamgee - 1 year ago 41
Python Question

Python - Parameter checking with Exception Raising

I am attempting to write exception raising code blocks into my Python code in order to ensure that the parameters passed to the function meet appropriate conditions (i.e. making parameters mandatory, type-checking parameters, establishing boundary values for parameters, etc...). I understand satisfactorily how to manually raise exceptions as well as handling them.

from numbers import Number

def foo(self, param1 = None, param2 = 0.0, param3 = 1.0):
if (param1 == None):
raise ValueError('This parameter is mandatory')
elif (not isinstance(param2, Number)):
raise ValueError('This parameter must be a valid Numerical value')
elif (param3 <= 0.0):
raise ValueError('This parameter must be a Positive Number')

This is an acceptable (tried and true) way of parameter checking in Python, but I have to wonder: Since Python does not have a way of writing Switch-cases besides if-then-else statements, is there a more efficient or proper way to perform this task? Or is implementing long stretches of if-then-else statements my only option?

Answer Source

You could create a decorator function and pass the expected types and (optional) ranges as parameters. Something like this:

def typecheck(types, ranges=None):
    def __f(f):
        def _f(*args, **kwargs):
            for a, t in zip(args, types):
                if not isinstance(a, t):
                    raise Exception("Expected %s got %r" % (t, a))
            if ranges:
                for a, r in zip(args, ranges):
                    if r and not r[0] <= a <= r[1]:
                        raise Exception("Should be in range %r: %r" % (r, a))
            return f(*args, **kwargs)
        return _f
    return __f


@typecheck(types=[int, float, str], ranges=[None, (0.0, 1.0), ("a", "f")])
def foo(x, y, z):
    print("called foo with ", x, y, z)

foo(10, .5, "b")  # called foo with  10 0.5 b
foo([1,2,3], .5, "b")  # Exception: Expected <class 'int'> got [1, 2, 3]
foo(1,2.,"e")  # Exception: Should be in range (0.0, 1.0): 2.0

You could also extend this to allow e.g. open ranges (like (0., None)) or to accept arbitrary (lambda) functions for more specific checks.