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
Pourquoi mettre à jour son Python (régulièrement) Pourquoi mettre à jour son Python (régulièrement) 03/12/2018

Le début des années 2010 a vu des centaines d’articles parler du passage de Python 2 à Python ...

Formation initiation Python à Nantes du 10 au 12 décembre Formation initiation Python à Nantes du 10 au 12 décembre 13/11/2018

Vous êtes développeur et maîtrisez déjà un langage de programmation ? Python vous tente et ...

Formation Python scientifique / Data scientist du 26 au 30 novembre à Toulouse Formation Python scientifique / Data scientist du 26 au 30 novembre à Toulouse 16/10/2018

Participez à notre prochaine session de formation Python scientifique à Toulouse !

10 choses qui me font aimer Python 10 choses qui me font aimer Python 08/10/2018

La 7e va vous surprendre ! Haha, non en fait il fallait que je m'arrête à 10, sinon vous ne ...

Machine Learning : classer automatiquement vos données à l'import Machine Learning : classer automatiquement vos données à l'import 20/03/2018

Comment utiliser des algorithmes de machine learning pour importer correctement des données dans ...