Accueil / Blog / Métier / Archives / Python check arguments types

Python check arguments types

Par Mathieu Leplatre publié 10/06/2010, édité le 11/03/2015
Decorators help us wrap some routines at function invocation

Decorators help us wrap some routines at function invocation. Here I show a small example that raises TypeError exceptions when given args have unexpected type. Note that it is not pythonic to type check.

This recipe is quite old, as its first pieces appear in PEP-0318 in 2003. A module exists too but it looks neglected...

The (heretic) decorator itself !

def accepts(*argstypes, **kwargstypes):
    def wrapper(func):
        def wrapped(*args, **kwargs):
            if len(args) > len(argstypes):
                raise TypeError("%s() takes at most %s non-keyword arguments (%s given)" % (func.__name__, len(argstypes), len(args)))
            argspairs = zip(args, argstypes)
            for k,v in kwargs.items():
                if k not in kwargstypes:
                    raise TypeError("Unexpected keyword argument '%s' for %s()" % (k, func.__name__))
                argspairs.append((v, kwargstypes[k]))
            for param, expected in argspairs:
                if param is not None and not isinstance(param, expected):
                    raise TypeError("Parameter '%s' is not %s" % (param, expected.__name__))
            return func(*args, **kwargs)
        return wrapped
    return wrapper

Let us decorate !

>>> @accepts(str, arg2=int)
... def f(arg1, arg2=None):
...     pass
...

See it in action...

>>> f('foo')
>>> f('foo', arg2=3)
>>> f()
TypeError: f() takes at least 1 argument (0 given)
>>> f('foo', 'bar')
TypeError: f() takes at most 1 non-keyword arguments (2 given)
>>> f('foo', arg2='bar')
TypeError: Parameter 'bar' is not int
>>> f('foo', arg3='bar')
TypeError: Unexpected keyword argument 'arg3' for f()

Or with classes...

>>> class A(object):
...     pass
...
>>> class B(object):
...     @accepts(object, (str, unicode))
...     def f(self, s):
...         pass
...
...     @accepts(object, A)
...     def g(self, a):
...         pass

>>>
>>> b = B()
>>> b.f(u'foo')
>>> b.f('foo')
>>> b.g(A())
>>> b.g(B())
TypeError: Parameter '<__main__.b object="" at="">' is not A

The same can be applied to return :)

def returns(rtype):
    def wrapper(f):
        def wrapped(*args, **kwargs):
            result = f(*args, **kwargs)
            if not isinstance(result, rtype):
                raise TypeError("return value %r does not match %s" % (result,rtype))
            return result
        return wrapped
    return wrapper
>>> @accepts(str, arg2=int)
... @returns(int)
... def f(arg1, arg2=None):
...     return 0
...

..but kids, don't do this at home :-)

ABONNEZ-VOUS À LA NEWSLETTER !
Voir aussi
Présentation de l'écosystème Python scientifique Présentation de l'écosystème Python scientifique 10/11/2016

Au fil des années Python est devenu un outil du quotidien pour les ingénieurs et chercheurs de ...

Monkey-patching a Python instance method 09/11/2016

Dynamically adding or overwriting an instance method in Python is rarely needed, but it's a good ...

Retour sur PyconFR 2013 05/11/2013

L'édition 2013 de la conférence Python française se tenait à Strasbourg du 26 au 29 Octobre. Je ...

Retour sur PyConFr 2015 Retour sur PyConFr 2015 19/10/2015

Makina Corpus était présent à Pau pour la PyConFr 2015, voici quelques retours à chaud.

Retour sur la PyConFr 2016 Retour sur la PyConFr 2016 18/10/2016

Nous étions présents à Rennes pour PyConFr 2016. Voici notre compte-rendu à chaud.