Python: Function Annotation and “inspect“ module

24 篇文章 0 订阅
14 篇文章 0 订阅

Function Annotation

https://peps.python.org/pep-3107/

Abstract

This PEP introduces a syntax for adding arbitrary metadata annotations to Python functions [1].

Rationale

Because Python’s 2.x series lacks a standard way of annotating a function’s parameters and return values, a variety of tools and libraries have appeared to fill this gap. Some utilise the decorators introduced in PEP 318, while others parse a function’s docstring, looking for annotations there.

This PEP aims to provide a single, standard way of specifying this information, reducing the confusion caused by the wide variation in mechanism and syntax that has existed until this point.

Fundamentals of Function Annotations

Before launching into a discussion of the precise ins and outs of Python 3.0’s function annotations, let’s first talk broadly about what annotations are and are not:

  1. Function annotations, both for parameters and return values, are completely optional.
  2. Function annotations are nothing more than a way of associating arbitrary Python expressions with various parts of a function at compile-time.

    By itself, Python does not attach any particular meaning or significance to annotations. Left to its own, Python simply makes these expressions available as described in Accessing Function Annotations below.

    The only way that annotations take on meaning is when they are interpreted by third-party libraries. These annotation consumers can do anything they want with a function’s annotations. For example, one library might use string-based annotations to provide improved help messages, like so:

    def compile(source: "something compilable",
                filename: "where the compilable thing comes from",
                mode: "is this a single statement or a suite?"):
        ...
    

    Another library might be used to provide typechecking for Python functions and methods. This library could use annotations to indicate the function’s expected input and return types, possibly something like:

    def haul(item: Haulable, *vargs: PackAnimal) -> Distance:
        ...
    

    However, neither the strings in the first example nor the type information in the second example have any meaning on their own; meaning comes from third-party libraries alone.

  3. Following from point 2, this PEP makes no attempt to introduce any kind of standard semantics, even for the built-in types. This work will be left to third-party libraries.

Syntax

Parameters

Annotations for parameters take the form of optional expressions that follow the parameter name:

def foo(a: expression, b: expression = 5):
    ...

In pseudo-grammar, parameters now look like identifier [: expression] [= expression]. That is, annotations always precede a parameter’s default value and both annotations and default values are optional. Just like how equal signs are used to indicate a default value, colons are used to mark annotations. All annotation expressions are evaluated when the function definition is executed, just like default values.

Annotations for excess parameters (i.e., *args and **kwargs) are indicated similarly:

def foo(*args: expression, **kwargs: expression):
    ...

Annotations for nested parameters always follow the name of the parameter, not the last parenthesis. Annotating all parameters of a nested parameter is not required:

def foo((x1, y1: expression),
        (x2: expression, y2: expression)=(None, None)):
    ...

Return Values

The examples thus far have omitted examples of how to annotate the type of a function’s return value. This is done like so:

def sum() -> expression:
    ...

That is, the parameter list can now be followed by a literal -> and a Python expression. Like the annotations for parameters, this expression will be evaluated when the function definition is executed.

The grammar for function definitions [11] is now:

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
funcdef: [decorators] 'def' NAME parameters ['->' test] ':' suite
parameters: '(' [typedargslist] ')'
typedargslist: ((tfpdef ['=' test] ',')*
                ('*' [tname] (',' tname ['=' test])* [',' '**' tname]
                 | '**' tname)
                | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
tname: NAME [':' test]
tfpdef: tname | '(' tfplist ')'
tfplist: tfpdef (',' tfpdef)* [',']

Lambda

lambda’s syntax does not support annotations. The syntax of lambda could be changed to support annotations, by requiring parentheses around the parameter list. However it was decided [12] not to make this change because:

  1. It would be an incompatible change.
  2. Lambdas are neutered anyway.
  3. The lambda can always be changed to a function.

Accessing Function Annotations

Once compiled, a function’s annotations are available via the function’s __annotations__ attribute. This attribute is a mutable dictionary, mapping parameter names to an object representing the evaluated annotation expression

There is a special key in the __annotations__ mapping, "return". This key is present only if an annotation was supplied for the function’s return value.

For example, the following annotation:

def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
    ...

would result in an __annotations__ mapping of

{'a': 'x',
 'b': 11,
 'c': list,
 'return': 9}

The return key was chosen because it cannot conflict with the name of a parameter; any attempt to use return as a parameter name would result in a SyntaxError.

__annotations__ is an empty, mutable dictionary if there are no annotations on the function or if the functions was created from a lambda expression.

Use Cases

In the course of discussing annotations, a number of use-cases have been raised. Some of these are presented here, grouped by what kind of information they convey. Also included are examples of existing products and packages that could make use of annotations.

  • Providing typing information
    • Type checking ([3][4])
    • Let IDEs show what types a function expects and returns ([16])
    • Function overloading / generic functions ([21])
    • Foreign-language bridges ([17][18])
    • Adaptation ([20][19])
    • Predicate logic functions
    • Database query mapping
    • RPC parameter marshaling ([22])
  • Other information
    • Documentation for parameters and return values ([23])

Standard Library

pydoc and inspect

The pydoc module should display the function annotations when displaying help for a function. The inspect module should change to support annotations.

Relation to Other PEPs

Function Signature Objects (PEP 362)

Function Signature Objects should expose the function’s annotations. The Parameter object may change or other changes may be warranted.

"inspect" module and its common methods

Inspect Module in Python - GeeksforGeeks

  • signature(): The signature() method helps the user in understanding the attributes which are to be passed on to a function. ==> by displaying them
  • isxxxx(arg): check whether the argument is a type of xxxx

for a comprehensive list of inspect's methods:

inspect — Inspect live objects — Python 3.10.7 documentation

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值