简体   繁体   English

Python3 的“函数注释”有什么好的用途?

[英]What are good uses for Python3's "Function Annotations"?

Function Annotations: PEP-3107 Function 注释: PEP-3107

I ran across a snippet of code demonstrating Python3's function annotations.我遇到了一段演示 Python3 的 function 注释的代码片段。 The concept is simple but I can't think of why these were implemented in Python3 or any good uses for them.这个概念很简单,但我想不出为什么这些是在 Python3 中实现的,或者它们有什么好的用途。 Perhaps SO can enlighten me?也许SO可以启发我?

How it works:这个怎么运作:

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

Everything following the colon after an argument is an 'annotation', and the information following the -> is an annotation for the function's return value.参数后冒号后面的所有内容都是“注释”, ->后面的信息是函数返回值的注释。

foo.func_annotations would return a dictionary: foo.func_annotations 会返回一个字典:

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

What's the significance of having this available?有这个可用的意义是什么?

Function annotations are what you make of them.函数注释是你对它们所做的。

They can be used for documentation:它们可用于文档:

def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second'):
     ...

They can be used for pre-condition checking:它们可用于先决条件检查:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        msg = 'Var: {0}\tValue: {1}\tTest: {2.__name__}'.format(var, value, test)
        assert test(value), msg


def is_int(x):
    return isinstance(x, int)

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    return _between

def f(x: between(3, 10), y: is_int):
    validate(f, locals())
    print(x, y)


>>> f(0, 31.1)
Traceback (most recent call last):
   ... 
AssertionError: Var: y  Value: 31.1 Test: is_int

Also see http://www.python.org/dev/peps/pep-0362/ for a way to implement type checking.另请参阅http://www.python.org/dev/peps/pep-0362/以了解实现类型检查的方法。

I think this is actually great.我认为这实际上很棒。

Coming from an academic background, I can tell you that annotations have proved themselves invaluable for enabling smart static analyzers for languages like Java.来自学术背景,我可以告诉你,注释已经证明它们对于为 Java 等语言启用智能静态分析器是非常宝贵的。 For instance, you could define semantics like state restrictions, threads that are allowed to access, architecture limitations, etc., and there are quite a few tools that can then read these and process them to provide assurances beyond what you get from the compilers.例如,您可以定义诸如状态限制、允许访问的线程、架构限制等语义,然后有相当多的工具可以读取这些并处理它们,以提供超出您从编译器获得的保证。 You could even write things that check preconditions/postconditions.您甚至可以编写检查前置条件/​​后置条件的内容。

I feel something like this is especially needed in Python because of its weaker typing, but there were really no constructs that made this straightforward and part of the official syntax.我觉得在 Python 中特别需要这样的东西,因为它的类型较弱,但实际上没有任何结构可以使这种简单明了并且成为官方语法的一部分。

There are other uses for annotations beyond assurance.超出保证的注释还有其他用途。 I can see how I could apply my Java-based tools to Python.我可以看到如何将基于 Java 的工具应用于 Python。 For instance, I have a tool that lets you assign special warnings to methods, and gives you indications when you call them that you should read their documentation (Eg, imagine you have a method that must not be invoked with a negative value, but it's not intuitive from the name).例如,我有一个工具可以让您为方法分配特殊警告,并在您调用它们时提示您应该阅读它们的文档(例如,假设您有一个不能用负值调用的方法,但它是从名称上看不直观)。 With annotations, I could technically write something like this for Python.有了注释,我可以在技术上为 Python 编写这样的东西。 Similarly, a tool that organizes methods in a large class based on tags can be written if there is an official syntax.同样,如果有官方语法,可以编写一个基于标签在大类中组织方法的工具。

This is a way late answer, but AFAICT, the best current use of function annotations is PEP-0484 and MyPy .这是一个迟到的答案,但 AFAICT,当前最好的函数注释使用是PEP-0484MyPy

Mypy is an optional static type checker for Python. Mypy 是 Python 的可选静态类型检查器。 You can add type hints to your Python programs using the upcoming standard for type annotations introduced in Python 3.5 beta 1 (PEP 484), and use mypy to type check them statically.您可以使用 Python 3.5 beta 1 (PEP 484) 中引入的即将推出的类型注释标准向 Python 程序添加类型提示,并使用 mypy 对它们进行静态类型检查。

Used like so:像这样使用:

from typing import Iterator

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b

Just to add a specific example of a good use from my answer here , coupled with decorators a simple mechanism for multimethods can be done.我想补充从我的回答很好地利用的一个具体的例子在这里,加上装饰可以做的多方法的简单机制。

# This is in the 'mm' module

registry = {}
import inspect

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function

def multimethod(function):
    name = function.__name__
    mm = registry.get(name)
    if mm is None:
        mm = registry[name] = MultiMethod(name)
    spec = inspect.getfullargspec(function)
    types = tuple(spec.annotations[x] for x in spec.args)
    mm.register(types, function)
    return mm

and an example of use:和一个使用示例:

from mm import multimethod

@multimethod
def foo(a: int):
    return "an int"

@multimethod
def foo(a: int, b: str):
    return "an int and a string"

if __name__ == '__main__':
    print("foo(1,'a') = {}".format(foo(1,'a')))
    print("foo(7) = {}".format(foo(7)))

This can be done by adding the types to the decorator as Guido's original post shows, but annotating the parameters themselves is better as it avoids the possibility of wrong matching of parameters and types.这可以通过将类型添加到装饰器来完成,如Guido 的原始帖子所示,但注释参数本身更好,因为它避免了参数和类型错误匹配的可能性。

Note : In Python you can access the annotations as function.__annotations__ rather than function.func_annotations as the func_* style was removed on Python 3.注意:在 Python 中,您可以将注释作为function.__annotations__而不是function.func_annotations因为func_*样式已在 Python 3 中删除。

Uri 已经给出了正确的答案,所以这里有一个不那么严肃的答案:所以你可以让你的文档字符串更短。

The first time I saw annotations, I thought "great! Finally I can opt in to some type checking!"我第一次看到注释时,我想“太好了!我终于可以选择进行某种类型检查了!” Of course, I hadn't noticed that annotations are not actually enforced.当然,我没有注意到注释实际上并没有被强制执行。

So I decided to write a simple function decorator to enforce them :所以我决定编写一个简单的函数装饰器来强制执行它们

def ensure_annotations(f):
    from functools import wraps
    from inspect import getcallargs
    @wraps(f)
    def wrapper(*args, **kwargs):
        for arg, val in getcallargs(f, *args, **kwargs).items():
            if arg in f.__annotations__:
                templ = f.__annotations__[arg]
                msg = "Argument {arg} to {f} does not match annotation type {t}"
                Check(val).is_a(templ).or_raise(EnsureError, msg.format(arg=arg, f=f, t=templ))
        return_val = f(*args, **kwargs)
        if 'return' in f.__annotations__:
            templ = f.__annotations__['return']
            msg = "Return value of {f} does not match annotation type {t}"
            Check(return_val).is_a(templ).or_raise(EnsureError, msg.format(f=f, t=templ))
        return return_val
    return wrapper

@ensure_annotations
def f(x: int, y: float) -> float:
    return x+y

print(f(1, y=2.2))

>>> 3.2

print(f(1, y=2))

>>> ensure.EnsureError: Argument y to <function f at 0x109b7c710> does not match annotation type <class 'float'>

I added it to the Ensure library.我将它添加到了确保库中。

It a long time since this was asked but the example snippet given in the question is (as stated there as well) from PEP 3107 and at the end of thas PEP example Use cases are also given which might answer the question from the PEPs point of view ;)自从提出这个问题已经很长时间了,但问题中给出的示例片段(也如此处所述)来自 PEP 3107,并且在 PEP 示例的末尾还给出了用例,这些用例可能会从 PEP 的角度回答问题看法 ;)

The following is quoted from PEP3107以下引自PEP3107

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])类型检查 ([3], [4])
    • Let IDEs show what types a function expects and returns ([17])让 IDE 显示函数期望和返回的类型([17])
    • Function overloading / generic functions ([22])函数重载/泛型函数 ([22])
    • Foreign-language bridges ([18], [19])外语桥梁([18]、[19])
    • Adaptation ([21], [20])适应([21]、[20])
    • Predicate logic functions谓词逻辑函数
    • Database query mapping数据库查询映射
    • RPC parameter marshaling ([23]) RPC 参数封送 ([23])
  • Other information其他信息
    • Documentation for parameters and return values ([24])参数和返回值的文档 ([24])

See the PEP for more information on specific points (as well as their references)有关特定点(及其参考)的更多信息,请参阅PEP

Python 3.X (only) also generalizes function definition to allow arguments and return values to be annotated with object values for use in extensions . Python 3.X(仅)还概括了函数定义,以允许使用对象值参数和返回值进行注释,以便在扩展中使用

Its META-data to explain, to be more explicit about the function values.它的元数据来解释,更明确地说明函数值。

Annotations are coded as :value after the argument name and before a default, and as ->value after the argument list.注释在参数名称之后和默认值之前编码为:value ,在参数列表之后编码为->value

They are collected into an __annotations__ attribute of the function, but are not otherwise treated as special by Python itself:它们被收集到函数的__annotations__属性中,但不被 Python 本身视为特殊的:

>>> def f(a:99, b:'spam'=None) -> float:
... print(a, b)
...
>>> f(88)
88 None
>>> f.__annotations__
{'a': 99, 'b': 'spam', 'return': <class 'float'>}

Source: Python Pocket Reference, Fifth Edition来源:Python Pocket Reference,第五版

EXAMPLE:例子:

The typeannotations module provides a set of tools for type checking and type inference of Python code. typeannotations模块提供了一组用于 Python 代码类型检查和类型推断的工具。 It also a provides a set of types useful for annotating functions and objects.它还提供了一组用于注释函数和对象的类型。

These tools are mainly designed to be used by static analyzers such as linters, code completion libraries and IDEs.这些工具主要设计用于静态分析器,例如 linter、代码完成库和 IDE。 Additionally, decorators for making run-time checks are provided.此外,还提供了用于进行运行时检查的装饰器。 Run-time type checking is not always a good idea in Python, but in some cases it can be very useful.运行时类型检查在 Python 中并不总是一个好主意,但在某些情况下它可能非常有用。

https://github.com/ceronman/typeannotations https://github.com/ceronman/typeannotations

How Typing Helps to Write Better Code打字如何帮助编写更好的代码

Typing can help you do static code analysis to catch type errors before you send your code to production and prevent you from some obvious bugs.键入可以帮助您在将代码发送到生产环境之前进行静态代码分析以捕获类型错误,并防止出现一些明显的错误。 There are tools like mypy, which you can add to your toolbox as part of your software life cycle.有像 mypy 这样的工具,您可以将其作为软件生命周期的一部分添加到您的工具箱中。 mypy can check for correct types by running against your codebase partially or fully. mypy 可以通过部分或全部运行您的代码库来检查正确的类型。 mypy also helps you to detect bugs such as checking for the None type when the value is returned from a function. mypy 还可以帮助您检测错误,例如在函数返回值时检查 None 类型。 Typing helps to make your code cleaner.打字有助于使您的代码更简洁。 Instead of documenting your code using comments, where you specify types in a docstring, you can use types without any performance cost.无需使用注释来记录您的代码,您可以在文档字符串中指定类型,而无需任何性能成本即可使用类型。

Clean Python: Elegant Coding in Python ISBN: ISBN-13 (pbk): 978-1-4842-4877-5 Clean Python:优雅的 Python 编码 ISBN:ISBN-13 (pbk):978-1-4842-4877-5

PEP 526 -- Syntax for Variable Annotations PEP 526 -- 变量注释的语法

https://www.python.org/dev/peps/pep-0526/ https://www.python.org/dev/peps/pep-0526/

https://www.attrs.org/en/stable/types.html https://www.attrs.org/en/stable/types.html

As a bit of a delayed answer, several of my packages (marrow.script, WebCore, etc.) use annotations where available to declare typecasting (ie transforming incoming values from the web, detecting which arguments are boolean switches, etc.) as well as to perform additional markup of arguments.作为一个有点延迟的答案,我的几个包(marrow.script、WebCore 等)也使用注释来声明类型转换(即转换来自网络的传入值,检测哪些参数是布尔开关等)以执行额外的参数标记。

Marrow Script builds a complete command-line interface to arbitrary functions and classes and allows for defining documentation, casting, and callback-derived default values via annotations, with a decorator to support older runtimes. Marrow Script 为任意函数和类构建了一个完整的命令行界面,并允许通过注释定义文档、转换和回调派生的默认值,并使用装饰器来支持旧的运行时。 All of my libraries that use annotations support the forms:我所有使用注释的库都支持以下形式:

any_string  # documentation
any_callable  # typecast / callback, not called if defaulting
(any_callable, any_string)  # combination
AnnotationClass()  # package-specific rich annotation object
[AnnotationClass(), AnnotationClass(), …]  # cooperative annotation

"Bare" support for docstrings or typecasting functions allows for easier mixing with other libraries that are annotation-aware.对 docstrings 或 typecasting 函数的“裸”支持允许更容易地与其他注释感知库混合。 (Ie have a web controller using typecasting that also happens to be exposed as a command-line script.) (即有一个使用类型转换的 web 控制器,它也恰好作为命令行脚本公开。)

Edited to add: I've also begun making use of the TypeGuard package using development-time assertions for validation.编辑添加:我还开始使用TypeGuard包,使用开发时断言进行验证。 Benefit: when run with "optimizations" enabled ( -O / PYTHONOPTIMIZE env var) the checks, which may be expensive (eg recursive) are omitted, with the idea that you've properly tested your app in development so the checks should be unnecessary in production.好处:在启用“优化”( -O / PYTHONOPTIMIZE env var)的情况下运行时,可能会很昂贵(例如递归)的检查被省略,因为您已经在开发中正确测试了您的应用程序,因此检查应该是不必要的在生产中。

Despite all uses described here, the one enforceable and, most likely, enforced use of annotations will be for type hints .尽管这里描述了所有用途,但一种可强制执行且最有可能强制使用的注解将用于类型提示

This is currently not enforced in any way but, judging from PEP 484, future versions of Python will only allow types as the value for annotations.这目前没有以任何方式强制执行,但从 PEP 484 来看,未来版本的 Python 将只允许类型作为注释的值。

Quoting What about existing uses of annotations?引用注释的现有用途呢? :

We do hope that type hints will eventually become the sole use for annotations, but this will require additional discussion and a deprecation period after the initial roll-out of the typing module with Python 3.5.我们确实希望类型提示最终会成为注释的唯一用途,但这将需要额外的讨论和在 Python 3.5 中最初推出类型模块后的弃用期。 The current PEP will have provisional status (see PEP 411 ) until Python 3.6 is released.在 Python 3.6 发布之前,当前的 PEP 将具有临时状态(参见 PEP 411)。 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.最快的方案将在 3.6 中引入对非类型提示注释的静默弃用,在 3.7 中完全弃用,并将类型提示声明为 Python 3.8 中唯一允许使用的注释。

Though I haven't seen any silent deprecations in 3.6 yet, this could very well be bumped to 3.7, instead.虽然我还没有在 3.6 中看到任何无声的弃用,但很可能会被撞到 3.7。

So, even though there might be some other good use-cases, it is best to keep them solely for type hinting if you don't want to go around changing everything in a future where this restriction is in place.因此,即使可能有其他一些好的用例,如果您不想在此限制到位的将来更改所有内容,最好将它们仅用于类型提示。

Annotations can be used for easily modularizing code.注释可用于轻松模块化代码。 Eg a module for a program which I'm maintaining could just define a method like:例如,我正在维护的程序模块可以定义一个方法,如:

def run(param1: int):
    """
    Does things.

    :param param1: Needed for counting.
    """
    pass

and we could ask the user for a thing named "param1" which is "Needed for counting" and should be an "int".我们可以向用户询问一个名为“param1”的东西,它是“需要计数”并且应该是“int”。 In the end we can even convert the string given by the user to the desired type to get the most hassle free experience.最后,我们甚至可以将用户给出的字符串转换为所需的类型,以获得最轻松的体验。

See our function metadata object for an open source class which helps with this and can automatically retrieve needed values and convert them to any desired type (because the annotation is a conversion method).请参阅我们的开源类的函数元数据对象,它有助于实现这一点,并且可以自动检索所需的值并将它们转换为任何所需的类型(因为注释是一种转换方法)。 Even IDEs show autocompletions right and assume that types are according to annotations - a perfect fit.甚至 IDE 也能正确显示自动补全,并假设类型符合注释——非常适合。

If you look at the list of benefits of Cython, a major one is the ability to tell the compiler which type a Python object is.如果您查看 Cython 的好处列表,主要的好处是能够告诉编译器 Python 对象是哪种类型。

I can envision a future where Cython (or similar tools that compile some of your Python code) will use the annotation syntax to do their magic.我可以设想未来 Cython(或编译您的某些 Python 代码的类似工具)将使用注释语法来发挥它们的魔力。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM