What are Function Annotations in Python?

What are Function Annotations in Python?

PEP is an acronym for Python Enhancement Proposal. This document outlines novel attributes of Python, its operations, and its ecosystem while furnishing information to the Python community. PEPs function as the principal channel for suggesting significant new attributes.

What are Function annotations?

Function annotations encompass arbitrary Python expressions linked with different components of functions. These expressions undergo evaluation during compile time and do not persist in Python’s runtime environment. These annotations lack inherent significance within Python itself. Instead, their purpose becomes evident when interpreted by external libraries, such as mypy, which breathe life into these annotations.

Function Annotations’ Purpose:

Function annotations yield benefits primarily through the utilization of third-party libraries. The nature of these benefits varies depending on the type of library employed. For instance,

Given Python’s support for dynamic typing, native modules for type checking are absent. Annotations like

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

(the syntax is elaborated in the following section.) prove useful for gathering insights into parameter types and function return types. These annotations facilitate the monitoring of type modifications within the function. An example of such a library is ‘mypy’.

String-based annotations, when harnessed by libraries, enhance compile-time assistance by providing comprehensive help messages. These messages shed light on the functionalities of diverse methods, classes, and modules.

Save $100 in the next
5:00 minutes?

Register Here

Syntax of function annotations:

They resemble the optional parameters that succeed the parameter name.

Note: The term ‘expression’ referenced below can represent the parameter type to be supplied, a comment, or any arbitrary string that external libraries can utilize meaningfully.

Annotations for Basic Parameters:

The parameter name is succeeded by ‘:’ followed by the expression. The annotation syntax is illustrated below.

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

Annotations for Varied Parameters:

Excess parameters such as *args and **kwargs enable passing an arbitrary number of arguments in a function call. The annotation syntax for these parameters is outlined below.

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

Annotations for Nested Parameters:

In Python 2.x, nested parameters involve passing a tuple in a function call, triggering automatic unpacking. This functionality is absent in Python 3.x, necessitating manual unpacking. As depicted below, annotations are applied after the variable, not the tuple.

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

Annotations for Return Type:

Annotating the return type slightly varies from annotating function arguments. The ‘->’ symbol is followed by an expression, subsequently followed by ‘:’. The annotation syntax for the return type is presented below.

 def foobar(a: expression) -> expression:

Grammar

decorator    :  ‘@’ name_  [‘(’ [arglist] ‘)’] NEWLINE
decorators   :  decorator+
funcdef      :  [decorators] ‘def’ NAME parameters [‘->’] ‘:’ suite
parameters   :  ‘(’ [typedarglist] ‘)’
typedarglist :  (( tfpdef [‘=’ test] ‘, ’)* (‘*’ [tname]
(‘, ’ tname [‘=’ test])* [‘, ’ ‘ **’ tname] | ‘**’ tname)
| tfpdef [‘=’ test (‘, ’ tfpdef [‘=’ test])* [‘, ’]])
tname        :  NAME [‘:’ test]
tfpdef       :  tname | ‘(’ tfplist ‘)’
tfplist      :  tfpdef (‘, ’ tfpdef)* [‘, ’]

Grammar Visualization:

The aforementioned grammar is employed to construct a parse tree, offering an enhanced visual representation of Python’s function and function annotations syntax.

Grammar Visualization

Example Code

The provided code clarifies that function annotations are not assessed during runtime. The code snippet generates and prints the Fibonacci series up to the specified ‘n’ positions.

 # Python program to print Fibonacci series
def fib(n:'int', output:'list'=[])-> 'list':
	if n == 0:
	return output
	else:
	if len(output)< 2:
		output.append(1)
		fib(n-1, output)
	else:
		last = output[-1]
		second_last = output[-2]
		output.append(last + second_last)
		fib(n-1, output)
	return output
print(fib(5))
Output 
[ 1, 1, 2, 3, 5 ]

Explanation:

The program defines a function called fib that takes two arguments:

n: An integer representing the number of Fibonacci numbers to generate.output: A list (with a default empty list []) that stores the Fibonacci series.Inside the fib function, there’s a base case:

If n equals 0, the function returns the output list as it is, which contains the generated Fibonacci series.

If n is not 0, the function proceeds with the Fibonacci sequence generation. It does this by checking the length of the output list.

If the length of the output list is less than 2, we are at the beginning of the sequence (0 and 1), so the function appends ‘1’ to the output list.

If the length of the output list is 2 or more, the function calculates the next Fibonacci number by adding the last two elements of the output list (i.e., the last and second-last numbers) and appends the result to the output list.

Then, the function recursively calls itself with n-1 and the updated output list to continue generating the Fibonacci series.

Finally, the output list is returned after the required Fibonacci numbers have been generated.

The program prints the result of calling the fib function with an argument of 5, which generates the first 5 Fibonacci numbers.

Note: Function annotations are only supported in Python 3x.

Conclusion:

In Python, function annotations enable the inclusion of optional type hints and metadata within function declarations. While they don’t impact runtime execution, these annotations enhance code understandability and documentation, serving as valuable aids for developers and tools such as linters and type checkers.

Save $100 in the next
5:00 minutes?

Register Here