Within PEP 3107 you can find the original PEP(Python Enhancement Proposals) for annotations of funcion parameters and return values.

This proposal that has been accepted is unfortunatelly only for Python3 and ongoing releases. Python2 out of the box backward compatibility is not possible.

Yet you can create some backward compatibility for those annotations if you need to.

Check out what this annotations are all about!

To The Point

Annotations

In other languages you have types. You expressly define your variable types, function return types and parameters types.

In Python 2 you could not do that (it would raise with SyntaxError) and frankly speaking you did not expect to do that.

In Python 3 you can use annotations to simulate defining function and variable types.

Here is example:

def greeting(name: str) -> str:
    return 'Hello ' + name

So basically you have created a function that in python2 would look like this:

def greeting(name):
    return 'Hello ' + name

But annotations gives you opportunity to check what type of output and parameter types you have within this function:

greeting.__annotations__

Unfortunatelly or fortunatelly it does not do anything else - So if you make annotations but you do not follow it - it will not raise it.

We can change that with decorator that will check that:

def decorator_annotation_for_return(function_wrapped):
    def wrapper(*args, **kwargs):
        annotations = function_wrapped.__annotations__
        output = function_wrapped(*args, **kwargs)
        returned_type = annotations.get('return')
        if returned_type:
            assert type(output) == returned_type

        return output
    return wrapper

Example of usage for this decorator:

@decorator_annotation_for_return
def greetings(name: str) -> str:
    return "Hello " + name

print(greetings("Anselmos"))

# This will fail:
@decorator_annotation_for_return
def greetings_failing(name: str) -> str:
    return 1

greetings_failing("A")

You can also create a parameter types checker with decorators, but in order for it to work properly you would need to use **kwargs - which means you need to expressively name arguments for function.

Check out example of that decorator:

def decorator_annotation_parameters(function_wrapped):
    def wrapper(*args, **kwargs):
        annotations = function_wrapped.__annotations__
        output = function_wrapped(*args, **kwargs)
        returned_type = annotations.get('return')
        for kwarg in kwargs:
            kwarg_type = annotations.get(kwarg)
            assert type(kwargs[kwarg]) == kwarg_type
        if returned_type:
            assert type(output) == returned_type

        return output
    return wrapper

@decorator_annotation_parameters
def greetings_parameter(name: str) -> str:
    return name


print(greetings_parameter(name=1))

It may need some tweaking to have better readiness but it works.

Also check out the suggested in pep Use Cases.

Snippets

def function(parameter: annotation_parameter_type) -> annotation_parameter_type:
    pass
def decorator_annotation_parameters(function_wrapped):
    def wrapper(*args, **kwargs):
        annotations = function_wrapped.__annotations__
        output = function_wrapped(*args, **kwargs)
        returned_type = annotations.get('return')
        for kwarg in kwargs:
            kwarg_type = annotations.get(kwarg)
            assert type(kwargs[kwarg]) == kwarg_type
        if returned_type:
            assert type(output) == returned_type

        return output
    return wrapper

Acknowledgements

Auto-promotion

Related links

Thanks!

That's it :) Comment, share or don't - up to you.

Any suggestions what I should blog about? Post me a comment in the box below or poke me at Twitter: @anselmos88.

See you in the next episode! Cheers!



Comments

comments powered by Disqus