代码之家  ›  专栏  ›  技术社区  ›  agscala

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

  •  144
  • agscala  · 技术社区  · 14 年前

    函数注释: PEP-3107

    我浏览了一段演示python3函数注释的代码。这个概念很简单,但我想不出为什么这些都是在python3中实现的,或者它们有什么好的用途。也许可以启发我?

    它是如何工作的:

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

    参数后面的冒号后面的所有内容都是“注释”,并且 -> 是函数返回值的注释。

    foo.func_注释将返回字典:

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

    提供这个有什么意义?

    12 回复  |  直到 6 年前
        1
  •  81
  •   Uri    14 年前

    我觉得这真的很棒。

    从学术背景来看,我可以告诉您,注释对于证明智能静态分析器对于Java这样的语言来说是非常宝贵的。例如,您可以定义语义,如状态限制、允许访问的线程、体系结构限制等,然后有相当多的工具可以读取这些内容并对其进行处理,以提供超出编译器所能提供的保证。你甚至可以写一些检查先决条件/后置条件的东西。

    我觉得在python中特别需要这样的东西,因为它的类型比较弱,但是实际上没有任何构造可以使这个简单化并成为官方语法的一部分。

    注释还有其他无法保证的用途。我可以看到如何将基于Java的工具应用到Python。例如,我有一个工具可以让您为方法分配特殊警告,并在您调用方法时给出指示,您应该阅读它们的文档(例如,假设您有一个方法,该方法不能用负值调用,但从名称上看是不直观的)。通过注释,我可以在技术上为Python编写类似这样的东西。类似地,如果有正式的语法,就可以编写一个基于标记在大类中组织方法的工具。

        2
  •  81
  •   Raymond Hettinger    13 年前

    函数注释就是你对它们所做的。

    它们可用于文档:

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

    它们可用于预状态检查:

    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
    

    也看到 http://www.python.org/dev/peps/pep-0362/ 用于实现类型检查的方法。

        3
  •  35
  •   Dustin Wyatt    9 年前

    这是一个迟来的答案,但目前对函数注释的最佳使用是 PEP-0484 MyPy .

    mypy是可选的python静态类型检查器。您可以使用Python3.5beta 1(pep 484)中引入的类型注释标准向Python程序添加类型提示,并使用mypy静态地对其进行类型检查。

    像这样使用:

    from typing import Iterator
    
    def fib(n: int) -> Iterator[int]:
        a, b = 0, 1
        while a < n:
            yield a
            a, b = b, a + b
    
        4
  •  23
  •   Community Romance    7 年前

    只是从我的答案中添加一个很好使用的具体例子 here ,再加上装饰器,可以完成多种方法的简单机制。

    # 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
    

    以及使用示例:

    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)))
    

    这可以通过将类型作为 Guido's original post 显示,但注释参数本身更好,因为这样可以避免参数和类型错误匹配的可能性。

    注释 :在python中,可以访问注释 function.__annotations__ 而不是 function.func_annotations 作为 func_* 在python 3上删除了样式。

        5
  •  20
  •   JAB    14 年前

    uri已经给出了一个正确的答案,所以这里有一个不那么严肃的答案:这样您就可以缩短docstrings。

        6
  •  12
  •   weaver    10 年前

    当我第一次看到注释时,我想“太好了!最后我可以选择某种类型的检查!”当然,我没有注意到注释实际上并没有被强制执行。

    所以我决定 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'>
    

    我把它添加到 Ensure 图书馆。

        7
  •  3
  •   klaas    10 年前

    很长一段时间以来,人们一直在问这个问题,但问题中给出的示例片段(如文中所述)来自PEP 3107,在THAS PEP的末尾,也给出了可能从PEP的角度回答问题的示例用例;)

    以下引自PEP3107

    用例

    在讨论注释的过程中,提出了一些用例。其中一些是在这里呈现的,按它们传递的信息类型分组。还包括可以使用注释的现有产品和包的示例。

    • 提供键入信息
      • 类型检查([3],[4])
      • 让IDES显示函数期望和返回的类型([17])
      • 函数重载/泛型函数([22])
      • 外语桥梁([18],[19])
      • 适应([21],[20])
      • 谓词逻辑函数
      • 数据库查询映射
      • RPC参数封送处理([23])
    • 其他信息
      • 参数和返回值的文档([24])

    PEP 有关特定点(及其引用)的详细信息

        8
  •  1
  •   Dimitris Fasarakis Hilliard    7 年前

    尽管这里描述了所有的用法,但最有可能强制使用注释的一种用法是 type hints .

    目前这并不是强制执行的,但是从PEP484来看,将来的Python版本只允许类型作为注释的值。

    引用 What about existing uses of annotations? :

    我们确实希望类型提示最终成为注释的唯一用途,但这需要在使用python 3.5的输入模块首次推出后进行额外的讨论和取消预测。在发布python 3.6之前,当前的PEP将具有临时状态(请参阅PEP 411)。最快的方案是在3.6中引入非类型提示注释的静默拒绝,在3.7中引入完全拒绝,并声明类型提示是Python3.8中唯一允许使用的注释。

    虽然我还没有在3.6中看到任何无声的贬低,但这很可能会被提升到3.7。

    因此,即使可能还有其他一些好的用例,如果您不想在未来改变所有的事情,而这个限制是适当的,那么最好将它们仅用于类型暗示。

        9
  •  1
  •   The Demz Ian    7 年前

    python 3.x(仅限)还将函数定义归纳为 要用对象值注释的参数和返回值 用于扩展 .

    它的元数据可以解释,更明确的是函数值。

    注释编码为 :value 后 参数名和在默认值之前,以及作为 ->value 后 参数列表。

    它们被收集到 __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'>}
    

    来源:python pocket reference,第五版

    例子:

    这个 typeannotations 模块为python代码的类型检查和类型推断提供了一套工具。它还提供了一组用于注释函数和对象的类型。

    这些工具主要设计用于静态分析器,如linters、代码完成库和ides。此外,还提供了用于进行运行时检查的装饰器。在Python中,运行时类型检查并不总是一个好主意,但在某些情况下,它可能非常有用。

    https://github.com/ceronman/typeannotations

        10
  •  1
  •   amcgregor    6 年前

    作为一个延迟的回答,我的几个包(mallow.script、webcore等)在可用的地方使用注释来声明类型转换(即转换来自web的传入值,检测哪些参数是布尔值开关等),以及执行参数的附加标记。

    骨髓脚本为任意函数和类构建了一个完整的命令行接口,并允许通过注释定义文档、强制转换和回调派生的默认值,使用一个装饰器来支持旧的运行时。所有使用注释的库都支持表单:

    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
    

    对docstring或类型转换函数的“裸”支持允许更容易地与其他支持注释的库混合。(例如,有一个使用类型转换的Web控制器,它碰巧也作为命令行脚本公开。)

    编辑添加: 我也开始利用 TypeGuard 使用开发时断言进行验证的包。优点:在启用“优化”的情况下运行时( -O / PYTHONOPTIMIZE env var)省去了代价高昂的检查(例如递归检查),因为您已经在开发中正确地测试了应用程序,因此在生产中不需要检查。

        11
  •  -2
  •   Lasse Schuirmann    9 年前

    注释可用于轻松模块化代码。例如,我维护的程序模块可以定义如下方法:

    def run(param1: int):
        """
        Does things.
    
        :param param1: Needed for counting.
        """
        pass
    

    我们可以向用户请求一个名为“param1”的东西,它是“计数所需的”,应该是“int”。最后,我们甚至可以将用户提供的字符串转换为所需的类型,以获得最轻松的体验。

    our function metadata object 对于一个开源类,它有助于实现这一点,并且可以自动检索所需的值并将其转换为 任何 所需类型(因为注释是转换方法)。即使是IDES也显示了自动完成的正确性,并且假定类型是根据注释来进行的——这是一个完美的匹配。

        12
  •  -2
  •   boardrider Patrick K    8 年前

    如果你看一下Cython的好处列表,一个主要的好处就是能够告诉编译器Python对象是哪种类型。

    我可以预见到将来,Cython(或编译一些Python代码的类似工具)将使用注释语法来实现它们的魔力。