Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python3'的好用途是什么;s";函数注释;?_Python_Function_Annotations_Python 3.x - Fatal编程技术网

Python3'的好用途是什么;s";函数注释;?

Python3'的好用途是什么;s";函数注释;?,python,function,annotations,python-3.x,Python,Function,Annotations,Python 3.x,函数注释: 我偶然发现了一段演示Python3函数注释的代码。这个概念很简单,但我想不出为什么这些都是在Python3中实现的,或者它们有什么好的用途。也许这样可以启发我 工作原理: def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9): ... function body ... 参数后冒号后面的所有内容都是“注释”,而->后面的信息是函数返回值的注释 foo.func_注释将返回字典: {'a': 'x', 'b': 11, 'c

函数注释:

我偶然发现了一段演示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}

这有什么意义呢?

我觉得这真的很棒

来自学术背景,我可以告诉您,注释在为Java等语言启用智能静态分析器方面证明了自己的价值。例如,您可以定义诸如状态限制、允许访问的线程、体系结构限制等语义,并且有很多工具可以读取这些语义并对其进行处理,以提供超出您从编译器获得的保证。你甚至可以写一些检查前置条件/后置条件的东西

我觉得Python中尤其需要这样的东西,因为它的类型较弱,但实际上没有构造使这一点变得简单,成为官方语法的一部分


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

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

只是为了从我的答案中添加一个很好使用的具体示例,加上装饰器,就可以实现多方法的简单机制

# 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)))
这可以通过向装饰器添加类型来实现,如图所示,但是注释参数本身更好,因为这样可以避免参数和类型错误匹配的可能性


注意:在Python中,您可以以
函数的形式访问注释。uu注释
而不是
函数。func注释
,因为在Python3上删除了
func*
样式。

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

它们可用于记录:

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
def验证(函数、局部变量):
对于var,在func.\uuuuu注释\uuuuu.items()中进行测试:
值=局部变量[var]
msg='Var:{0}\t值:{1}\t测试:{2.\u__名称\u_}'。格式(Var,值,测试)
断言测试(值),msg
def为_int(x):
返回isinstance(x,int)
def介于(低、高)之间:
def_介于(x)之间:
返回lo>f(0,31.1)
回溯(最近一次呼叫最后一次):
... 
断言错误:Var:y值:31.1测试:is_int

另请参阅以了解实现类型检查的方法。

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

所以我决定:

def确保注释(f):
从functools导入包装
从检查导入getcallargs
@包装(f)
def包装(*args,**kwargs):
对于arg,getcallargs中的val(f,*args,**kwargs)。items():
如果f.\uuuu注释中有arg:
templ=f.\uuuuu注释\uuuuu[arg]
msg=“参数{arg}到{f}与注释类型{t}不匹配”
检查(val).is_a(templ.)或_raise(EnsureError,msg.format(arg=arg,f=f,t=templ))
返回值=f(*args,**kwargs)
如果f.\uuuu注释中的“return”:
templ=f.。\uuuuuu注释\uuuu['return']
msg=“返回值{f}与注释类型{t}不匹配”
检查(返回值)。是否为a(模板)。或升高(确保错误,消息格式(f=f,t=模板))
返回值
返回包装器
@确保添加注释
def(x:int,y:float)->float:
返回x+y
打印(f(1,y=2.2))
>>> 3.2
打印(f(1,y=2))
>>>Survey.EnsureError:参数y与注释类型不匹配

我将其添加到库中。

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

以下引自PEP3107

用例

在讨论注释的过程中,提出了许多用例。这里列出了其中一些,根据它们所传达的信息类型进行分组。还包括可以使用注释的现有产品和包的示例

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

有关特定点(以及它们的引用)的更多信息,请参见。

作为一个有点延迟的回答,我的几个包(marow.script、WebCore等)在可用的地方使用注释来声明类型转换(即转换来自web的传入值,检测哪些参数是布尔值)
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'>
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
>>> def f(a:99, b:'spam'=None) -> float:
... print(a, b)
...
>>> f(88)
88 None
>>> f.__annotations__
{'a': 99, 'b': 'spam', 'return': <class 'float'>}
from typing import Iterator

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