934

I've recently noticed something interesting when looking at Python 3.3 grammar specification:

funcdef: 'def' NAME parameters ['->' test] ':' suite

The optional 'arrow' block was absent in Python 2 and I couldn't find any information regarding its meaning in Python 3. It turns out this is correct Python and it's accepted by the interpreter:

def f(x) -> 123:
    return x

I thought that this might be some kind of a precondition syntax, but:

  • I cannot test x here, as it is still undefined,
  • No matter what I put after the arrow (e.g. 2 < 1), it doesn't affect the function behavior.

Could anyone familiar with this syntax style explain it?

John Smith
  • 7,243
  • 6
  • 49
  • 61
Krotton
  • 9,561
  • 3
  • 15
  • 12
  • 1
    If you find this in any code nowadays, it's probably a type hint for a static type checker like mypy. https://www.mypy-lang.org/ – Joooeey Apr 23 '23 at 17:01

11 Answers11

693

It's a function annotation.

In more detail, Python 2.x has docstrings, which allow you to attach a metadata string to various types of object. This is amazingly handy, so Python 3 extends the feature by allowing you to attach metadata to functions describing their parameters and return values.

There's no preconceived use case, but the PEP suggests several. One very handy one is to allow you to annotate parameters with their expected types; it would then be easy to write a decorator that verifies the annotations or coerces the arguments to the right type. Another is to allow parameter-specific documentation instead of encoding it into the docstring.

Katriel
  • 120,462
  • 19
  • 136
  • 170
  • 185
    And the information is available as a `.__annotations__` attribute. – Martijn Pieters Jan 17 '13 at 13:06
  • 15
    Wow, I missed quite a broad area of knowledge - not only return value annotations, but also parameter annotations. Thank you very much :). – Krotton Jan 17 '13 at 13:16
  • 6
    @Krotton Can't blame you for missing it, it's practically unused. I only ever met a single library using them, and it's quite obscure. –  Jan 17 '13 at 13:19
  • 9
    And the `__annotations__` attribute is a dictionary. The key `return` is the one used to retrieve the value after the arrow. – Keith Jan 17 '13 at 13:20
  • 10
    @delnan -- probably the reason that it's mostly unused is because most python libraries still aim to be compatible with python2.x. As python3.x begins to become more standard, we might see more of these things popping up here and there... – mgilson Jan 17 '13 at 14:15
  • 1
    In python 3.5 they added "type hinting", as described in PEP484 https://www.python.org/dev/peps/pep-0484/ – matan7890 Nov 30 '15 at 11:07
  • Would love to see examples – Jonathan Jan 03 '19 at 19:43
  • @user395760, could you please add a link to the library that uses `function annotations` extensively! – Anu Jul 20 '19 at 21:01
  • To specify parameter types, you put them after the variable name like so: `def foo(bar: int)`, in this case specifying that bar is an int. – Gabriel Petersson Apr 17 '20 at 15:29
  • @Anu The huggingface does so. There's actually a lot of really great OOP code in here I wasn't familiar with in Python: https://github.com/huggingface/transformers/blob/d9a81fc0c5d8339357a42435009a5be3a190b305/src/transformers/hf_argparser.py#L134 – vsocrates Feb 19 '21 at 00:59
  • One thing that I don't understand is here https://peps.python.org/pep-3107/#accessing-function-annotations, the given example is `def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):`. Why would a function annotation have an actual expression? Instead of say, a string or a type, which can help a coder know what the parameter does or accepts? What is its usecase of having expressions here? – Ativerc Mar 30 '23 at 08:04
607

These are function annotations covered in PEP 3107. Specifically, the -> marks the return function annotation.

Examples:

def kinetic_energy(m:'in KG', v:'in M/S')->'Joules': 
    return 1/2*m*v**2
 
>>> kinetic_energy.__annotations__
{'return': 'Joules', 'v': 'in M/S', 'm': 'in KG'}

Annotations are dictionaries, so you can do this:

>>> '{:,} {}'.format(kinetic_energy(12,30),
      kinetic_energy.__annotations__['return'])
'5,400.0 Joules'

You can also have a python data structure rather than just a string:

rd={'type':float,'units':'Joules',
    'docstring':'Given mass and velocity returns kinetic energy in Joules'}
def f()->rd:
    pass

>>> f.__annotations__['return']['type']
<class 'float'>
>>> f.__annotations__['return']['units']
'Joules'
>>> f.__annotations__['return']['docstring']
'Given mass and velocity returns kinetic energy in Joules'

Or, you can use function attributes to validate called values:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        try: 
            pr=test.__name__+': '+test.__docstring__
        except AttributeError:
            pr=test.__name__   
        msg = '{}=={}; Test: {}'.format(var, value, pr)
        assert test(value), msg

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    _between.__docstring__='must be between {} and {}'.format(lo,hi)       
    return _between

def f(x: between(3,10), y:lambda _y: isinstance(_y,int)):
    validate(f, locals())
    print(x,y)

Prints

>>> f(2,2) 
AssertionError: x==2; Test: _between: must be between 3 and 10
>>> f(3,2.1)
AssertionError: y==2.1; Test: <lambda>
dawg
  • 98,345
  • 23
  • 131
  • 206
  • This is extremely useful, thank you! Although, as I remember the underscore `_` allows you to call the last function/variable but I'm wondering what `lambda` in this case as `_y` doing? Also, can `_between` be replaced with just `_`? – Benjamin Diaz Jul 05 '22 at 19:09
  • 1
    In both cases, the `_` is a shortcut to *I kinda mean the same name as [that] but I don't want to figure out if the namespace allows me to use the same name or it would be confusing.* With `between` and the inner function `_between` it is not needed but confusing if the same name. These could be the same names. With the lambda, you have the name (key of a dict) of the lambda function and the name of the argument. Again, similar but confusing if the same name. – dawg Jul 05 '22 at 20:38
301

In the following code:

def f(x) -> int:
    return int(x)

the -> int just tells that f() returns an integer (but it doesn't force the function to return an integer). It is called a return annotation, and can be accessed as f.__annotations__['return'].

Python also supports parameter annotations:

def f(x: float) -> int:
    return int(x)

: float tells people who read the program (and some third-party libraries/programs, e. g. pylint) that x should be a float. It is accessed as f.__annotations__['x'], and doesn't have any meaning by itself. See the documentation for more information:

https://docs.python.org/3/reference/compound_stmts.html#function-definitions https://www.python.org/dev/peps/pep-3107/

Maximouse
  • 4,170
  • 1
  • 14
  • 28
130

As other answers have stated, the -> symbol is used as part of function annotations. In more recent versions of Python >= 3.5, though, it has a defined meaning.

PEP 3107 -- Function Annotations described the specification, defining the grammar changes, the existence of func.__annotations__ in which they are stored and, the fact that it's use case is still open.

In Python 3.5 though, PEP 484 -- Type Hints attaches a single meaning to this: -> is used to indicate the type that the function returns. It also seems like this will be enforced in future versions as described in What about existing uses of annotations:

The fastest conceivable scheme would introduce silent deprecation of non-type-hint annotations in 3.6, full deprecation in 3.7, and declare type hints as the only allowed use of annotations in Python 3.8.

(Emphasis mine)

This hasn't been actually implemented as of 3.6 as far as I can tell so it might get bumped to future versions.

According to this, the example you've supplied:

def f(x) -> 123:
    return x

will be forbidden in the future (and in current versions will be confusing), it would need to be changed to:

def f(x) -> int:
    return x

for it to effectively describe that function f returns an object of type int.

The annotations are not used in any way by Python itself, it pretty much populates and ignores them. It's up to 3rd party libraries to work with them.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
14

This means the type of result the function returns, but it can be None.

It is widespread in modern libraries oriented on Python 3.x.

For example, it there is in code of library pandas-profiling in many places for example:

def get_description(self) -> dict:

def get_rejected_variables(self, threshold: float = 0.9) -> list:

def to_file(self, output_file: Path or str, silent: bool = True) -> None:
"""Write the report to a file.
Vitalii
  • 169
  • 1
  • 8
10
def f(x) -> 123:
    return x

My summary:

  1. Simply -> is introduced to get developers to optionally specify the return type of the function. See Python Enhancement Proposal 3107

  2. This is an indication of how things may develop in future as Python is adopted extensively - an indication towards strong typing - this is my personal observation.

  3. You can specify types for arguments as well. Specifying return type of the functions and arguments will help in reducing logical errors and improving code enhancements.

  4. You can have expressions as return type (for both at function and parameter level) and the result of the expressions can be accessed via annotations object's 'return' attribute. annotations will be empty for the expression/return value for lambda inline functions.

TheTechRobo the Nerd
  • 1,249
  • 15
  • 28
maz
  • 101
  • 1
  • 3
10
def f(x) -> str:
return x+4

print(f(45))

Will give the result : 49.

Or in other words '-> str' has NO effect on return type:

print(f(45).__class__)

<class 'int'>
Mate Mrše
  • 7,997
  • 10
  • 40
  • 77
everyt4u
  • 314
  • 3
  • 5
8

def function(arg)->123:

It's simply a return type, integer in this case doesn't matter which number you write.

like Java :

public int function(int args){...}

But for Python (how Jim Fasarakis Hilliard said) the return type it's just an hint, so it's suggest the return but allow anyway to return other type like a string..

Mike D3ViD Tyson
  • 1,701
  • 1
  • 21
  • 31
6

-> is introduced in python3.

In simpler words, the content after the -> denotes the return type of the function. The return type is optional.

vignesh
  • 77
  • 1
  • 2
  • 2
    How does the guidance in this answer differ from the guidance in the previous answers already posted? – Hoppeduppeanut Jun 10 '21 at 06:14
  • The return type might still be different, the annotation is like an expression that explains the return value of the function, but if for example we put str after '->' but we return an int, python would not give any error. – typedecker Jan 08 '22 at 13:30
4

It's just telling the user what it expects or return the value

funcname.__annotations__ will print the details

like

def function(name:str ,age:int) -> "printing the personal details ":
    print(f"name is {name} age is {age}")

function("test",20)
print(function.__annotations__)

The Output

name is test age is 20
{'name': <class 'str'>, 'age': <class 'int'>, 'return': 'printing the personal details '}

even when you return the values it display nothing.

Hornbydd
  • 321
  • 2
  • 12
Krunal Akbari
  • 316
  • 1
  • 17
0

Please refer to the PEP3107 specification. These are function annotations. Python 2.x has docstrings. Similarly, Python 3 introduced the use of -> as function annotations. Python uses these while generating documentation.