3
\$\begingroup\$

I'd like to know your opinions on this minimal type-checking decorator (with @ annotations) to make type checking of a method while debugging like :

def typecheck(*tuples, **kwtuples): def decorator(func): def function_wrapper(*args, **kwargs): #print('tuples : ' , tuples) #print('kwtuples : ' , kwtuples) #print('args : ' , args) #print('kwargs : ' , kwargs) for index, tup in enumerate(tuples): arg = args[index] if not isinstance(arg, tup): raise ValueError('in ' + str(func.__name__) + ' : wrong argument on position ,' + str(index) + ' :' + str(arg) + ' must be of type :' + str(tup) + 'but is' + str(type(arg)) ) for key, tup in kwtuples.items(): arg = kwargs[key] if not isinstance(args[index], tup): raise ValueError('in ' + str(func.__name__) + ' : wrong argument ' + str(key) + ' :' + str(arg) + ' must be of type :' + str(tup) + 'but is' + str(type(arg)) ) #print("arguments: ", *args, **kwargs) func(*args, **kwargs) return function_wrapper return decorator @typecheck(str,(str, float)) def foo2(x,y): print("Hi, foo has been called with ",str(x) ,y) foo2('2',3.4) 

The benefit of it is :

  • Custom and detailed description about the wrong argument
  • Later, you can extend this for custom validation like some item is in some range or structural inspection of an argument.
  • Easy to write, apply and delete after testing (so it won't consume cpu time)
\$\endgroup\$
3
  • 2
    \$\begingroup\$Have you considered somehow taking advantage of type hints, introduced in Python 3.5? (And if not, why?)\$\endgroup\$CommentedMay 27, 2019 at 18:39
  • 2
    \$\begingroup\$In addition to @200_success other than using typing and mypy/pyre/pyright/pytype for static type enforcement, you can also use something like pytypes for run-time type enforcement.\$\endgroup\$
    – Peilonrayz
    CommentedMay 27, 2019 at 18:43
  • \$\begingroup\$those stuff do not custom value checking or structural checking, dont they? also i want it to be easy removed after testing\$\endgroup\$CommentedMay 27, 2019 at 19:08

1 Answer 1

5
\$\begingroup\$

To amplify @200_success’s comment:

Python 3.5 introduces type hints. Eg)

>>> def foo2(x:str, y:float) -> None: ... print(f"Hi, foo has been called with {x} {y}") ... >>> foo2("2", 3.4) Hi, foo has been called with 2 3.4 >>> 

These type hints (the x:str, and y:float) are not used by the standard Python interpreter, but they are recorded during the parsing of the code, so they are available to the program.

>>> foo2.__annotations__ {'return': None, 'x': <class 'str'>, 'y': <class 'float'>} >>> 

Directly accessing the foo2.__annotations__ is not that interesting, but accessing func.__annotations__ in the @typecheck decorator means you don’t have to provide the argument types to the decorator; the decorator can inspect the argument types directly. Thus you could decorate the function with simply @typecheck, instead of @typecheck(str, float).

For a variant str or float type argument, you could use the typing module to define your own STR_OR_FLOAT = TypeVar("STR_OR_FLOAT", str, float) type (or a better name if you have one), which you could decorate the argument with.

As a bonus, using type hints to provide argument type checking information — even for your do-it-yourself via a function decorator type check system — also gives you the following benefits:

  • IDE type hints, as you are writing code
  • Can be understood by standard documentation generators
  • Only consumes CPU time during parsing of the code; does not affect execution speed
\$\endgroup\$

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.