EquipDev
EquipDev

Reputation: 5941

Simple dynamic type checking

Is there a "standard" way to add simple dynamic type checking in Python, doing something like:

def add(a, b):
    # Argument type check
    check(a, int)
    check(b, int)
    # Calculate
    res = a + b
    # Result type check and return 
    check(res, int)
    return res

An exception could then be raised by check in case of a type mismatch.

I could of course cook something myself, doing isinstance(..., ...) or type(...) == ..., but I wonder if there is some "standard" module for this kind of type checking.

It would be nice if more complex type checking could also be done, like checking if an argument is either str or int, or for example a list of str.

I am aware that it somehow defies Pythons principle of duck typing, but I just spent several hours debugging due to an argument with wrong type, and it was a large program, so the cause shown up many nested calls from the reason.

Upvotes: 2

Views: 1270

Answers (2)

tobias_k
tobias_k

Reputation: 82949

You could use a decorator function. Something like this:

def typecheck(*types):
    def __f(f):
        def _f(*args):
            for a, t in zip(args, types):
                if not isinstance(a, t):
                    print "WARNING: Expected", t, "got", a
            return f(*args)
        return _f
    return __f

@typecheck(int, str, int)
def foo(a, b, c):
    pass    

foo(1, "bar", 5)
foo(4, None, "string")

Output (for the second call) is

WARNING: Expected <type 'str'>, got None 
WARNING: Expected <type 'int'>, got 'string' 

As it stands, this does not work for keyword parameters, though.

Edit: After some googling, I found some much more complex type checking decorators (1) (2) also supporting keyword parameters and return types.

Upvotes: 4

Noufal Ibrahim
Noufal Ibrahim

Reputation: 72855

There is mypy which is being considered for entry into Python proper but in general, there isn't any way to do what you want.

Your code should not depend on concrete types but on general interfaces (e.g. not whether two things are integers but whether they are "addable"). This allows you to take advantage of dynamic typing and write generic functions. If a type does not handle the interface you want, it will throw an exception which you can catch. So, your add would be better done like so.

def add(a, b):
  try:
    return a + b
  except TypeError as t:
    # some logging code here for your debugging ease
    raise t

If you are on Python 3, there is optional type annotation for functions. This means that the following code is valid Python 3.

def add(a:int, b:int):
   return a + b

I don't know if there any tools that take advantage of the hints to give you actual compile time checking though.

Upvotes: 2

Related Questions