I wrote a decorator which makes Python 3.5 raise exceptions if the arguments that are passed to a type-hinted function are of the wrong type.from typing import get_type_hintsdef strict_types(f):    def type_checker(*args, **kwargs):        hints = get_type_hints(f)        all_args = kwargs.copy()        all_args.update(dict(zip(f.__code__.co_varnames, args)))        for key in all_args:            if key in hints:                if type(all_args[key]) != hints[key]:                    raise Exception('Type of {} is {} and not {}'.format(key, type(all_args[key]), hints[key]))        res = f(*args, **kwargs)        if type(res) == hints['return']:            return res        else:            raise Exception('Type of result is {} and not {}'.format(type(res), hints['return']))    return type_checkerIt's used like this:@strict_typesdef concatenate_with_spam(text: str) -> str:    return text + 'spam'and if something of the wrong type is passed to the decorated function e.g. concatenate_with_spam(42) an exception is raised:Exception: Type of text is <class 'int'> and not <class 'str'>The part that I'm worried about is the part that checks the result. It calls the original function (to learn result of what type it returns), but returns a wrapped function type_checker that will be called one more time, so the runtime of function is (at least) multiplied by 2. Are there any other things I have missed? What can be improved here (except for not using this not-pythonic decorator and crucifying me for writing it)?
Strict types decorator (works only with Python 3.5)
python;python 3.x;reflection;meta programming;type safety
You should change this part a bit:    for key in all_args:        if key in hints:            if type(all_args[key]) != hints[key]:                raise Exception('Type of {} is {} and not {}'.format(key, type(all_args[key]), hints[key]))Here, you are silently ignoring missing type hints, but if the user went as far as to use this decorator, you should raise an exception instead, like this:    for key in all_args:        try:            if type(all_args[key]) != hints[key]:                raise Exception('Type of {} is {} and not {}'.format(key, type(all_args[key]), hints[key]))        except IndexError:                raise TypeError('The formal parameter {} was not given a type'.format(key))Specific Errors & ExceptionsException is hilariously un-informative, use TypeError to convey more information.Order of conditionals    if type(res) == hints['return']:        return res    else:        raise Exception('Type of result is {} and not {}'.format(type(res), hints['return']))Here, the real logic is in the else, but I prefer seeing the main logic after the if, like this:if type(res) != hints['return']:    raise Exception('Type of result is {} and not {}'.format(type(res), hints['return']))return resNow it looks more like a simple perturbation of an almost linear function, than a full-blown logic statement.Please do not abbreviateres -> result func -> functionIt takes 0.5 seconds more to type and a tenth of the time to read.