Python3'的好用途是什么;s";函数注释;?
函数注释: 我偶然发现了一段演示Python3函数注释的代码。这个概念很简单,但我想不出为什么这些都是在Python3中实现的,或者它们有什么好的用途。也许这样可以启发我 工作原理: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
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