/    /  Python – Function annotations

Python – Function annotations

In this tutorial, we will learn python in detail.

 

Function annotations are associated with various part of functions and are arbitrary python expressions. These expressions are assessed at compile time and have no life in python’s runtime. Python does not attach any sense to these annotations. They take life when interpreted by third party libraries, for example, mypy.

 

Purpose of function annotations:

 

  1. Python supports dynamic typing and hence no module is provided for type checking. Annotations like

 

[def foo(a:”int”, b:”float”=5.0)  -> ”int”]

 

can be used to collect information about the type of the parameters and the return type of the function to keep track of the type change occurring in the function. ‘mypy’ is one such library.

 

  1. String based annotations can be used by the libraries to provide better help messages at compile time regarding the functionalities of various methods, classes and modules.

 

Syntax of function annotations

 

They are like the optional parameters that follow the parameter name.

 

The word ‘expression’ mentioned below can be the type of the parameters that should be passed or comment or any arbitrary string that can be made use by external libraries in a meaningful way.

 

  1. Annotations for simple parameters:The argument name is followed by ‘:’ which is then followed by the expression. Annotation syntax is shown below.

 

def foobar(a: expression, b: expression = 5):

 

  1. Annotations for excess parameters :Excess parameters for e.g. *args and **kwargs, allow arbitrary number of arguments to be passed in a function call. Annotation syntax of such parameters is shown below.

 

def foobar(*args: expression, *kwargs: expression):

 

  1. Annotations for nested parameters: Nested parameters are useful feature of python 2x where a tuple is passed in a function call and automatic unpacking takes place. This feature is removed in python 3x and manual unpacking has to be done.

 

Annotation is not done after the tuple, but is done the variable as shown below.

 

def foobar((a: expression, b: expression), (c: expression, d: expression)):

 

  1. Annotations for return type:Annotating return type is slightly different from annotating function arguments. The ‘->’ is followed by expression which is further followed by ‘:’. Annotation syntax of return type is shown below.

 

def foobar(a: expression)->expression:

 

Accessing Function Annotations

 

All the annotations are stored in a dictionary called __annotations__, in which itself is an attribute of the function –

 

>>> def func(x:'annotating x', y: 'annotating y', z: int) -> float: print(x + y + z)
>>> func.__annotations__
{'x': 'annotating x', 'y': 'annotating y', 'z': <class 'int'>, 'return': <class 'float'>}

 

As we can see in the above code example, annotations are not typed declarations, though they could definitely be used for that purpose and they look like the typing syntax used in some other languages, as shown below –

 

>>> def func(a: 'python', b: {'category: ' 'language'}) -> 'yep':
   pass
>>> func.__annotations__
{'a': 'python', 'b': {'category: language'}, 'return': 'yep'}
>>> 

 

They are just arbitrary expressions, which means that arbitrary values can be stored in the __annotations__ dictionary. It just stores the values, they don’t add much significance to python. Defining the parameter and return types is a general use of function annotations.

 

The @no_type_check decorator

 

If you find yourselves using a tool that assumes annotations are type declarations but you want to use them for some other purpose, use the standard @no_type_check decorator in order to exempt your function from such processing, as shown below–

 

>>> from typing import no_type_check
>>> @no_type_check
def func(a: 'python', b: {'category: ' 'language'}) -> 'yep':
  pass
>>> 

 

Generally, this isn’t required as most tools that use annotations has a way of identifying the ones meant for them.