-
Notifications
You must be signed in to change notification settings - Fork 3
Python
Starting in Python3 we can define types hints in functions, it looks like this:
def f(x: str, y):
print(type(x), type(y))
Notice we added the str
type. This is called a hint because it doesn't mean anything for Python, we can call our function with any type:
>>> f(3, 4)
<class 'int'> <class 'int'>
These hints have been introduced to let programmers define even more awesome libraries. We're going to write a decorator (something that change the behavior of our function) to force the casting into this type. That way, any time we call
f(x, y)
We would actually be calling
f(str(x), y)
Let's define the following decorator (packages are in the default library).
import functools
import inspect
# This is our decorator, it's a function that takes original
# function (func) and return a modified function (wrapper)
def apply_type(func):
# This decorator let us transpose the meta information
# of func on our new function wrapper
@functools.wraps(func)
# This is the new function we're creating. It takes
# whatever we give it (*args, **kwargs)
def wrapper(*args, **kwargs):
# We inspect the signature of func
sig = inspect.signature(func)
# This let us get a parameter name for every argument
# even if it's apply positionally. It's the resolution
# Python does.
bind = sig.bind(*args, **kwargs)
# Looping through all arguements name and value
for name, val in bind.arguments.items():
# We get the annotation (the type hint)
ann = sig.parameters[name].annotation
# If there is a type hint
if ann is not inspect._empty:
# Then we apply the type hint to the input
bind.arguments[name] = ann(val)
# We apply the original function with the modified
# arguments
return func(*bind.args, **bind.kwargs)
# We return the modified function
return wrapper
Now, let's try it
# This notation just means
# f = apply_type(f)
@apply_type
def f(x: str, y):
print(type(x), type(y))
And now we have:
>>> f(3, 4)
<class 'str'> <class 'int'>
Hurray !!
Now some ideas where this could get very useful:
- If you want to use a file path with the great
pathlib
but you also want to let a user give a string, this will convert it for you@apply_type def f(path: pathlib.Path): ...
- Sometimes your algorithm need to work with an
int
but you want to be able to also take afloat
(because you know it makes sense). This function will manage that for youIf you work with numpy, your functions can also get some annoying@apply_type def f(x: int): ...
numpy.int64
that make your program bug. This is also a defense against that.
This decorator can be modified freely, for instance you could make a decorator that raise an exception if the given parameter if not of the declared type.