标签归档:annotations

Python void返回类型注释

问题:Python void返回类型注释

在python 3.x中,通常使用函数的返回类型注释,例如:

def foo() -> str:
    return "bar"

“ void”类型的正确注释是什么?

我正在考虑3个选择:

  1. def foo() -> None:
    • 不是逻辑IMO,因为None不是类型,
  2. def foo() -> type(None):
    • 使用我知道的最佳语法NoneType
  3. def foo():
    • 省略显式的返回类型信息。

选项2对我来说似乎是最合乎逻辑的,但是我已经看到过一些实例1。

In python 3.x, it is common to use return type annotation of a function, such as:

def foo() -> str:
    return "bar"

What is the correct annotation for the “void” type?

I’m considering 3 options:

  1. def foo() -> None:
    • not logical IMO, because None is not a type,
  2. def foo() -> type(None):
    • using the best syntax I know for obtaining NoneType,
  3. def foo():
    • omit explicit return type information.

Option 2. seems the most logical to me, but I’ve already seen some instances of 1.


回答 0

这直接来自PEP 484-类型提示文档:

在类型提示中使用时,该表达式None被认为等效于type(None)

而且,如您所见,大多数示例都None用作返回类型。

This is straight from PEP 484 — Type Hints documentation:

When used in a type hint, the expression None is considered equivalent to type(None).

And, as you can see most of the examples use None as return type.


回答 1

TLDR:void返回类型注释的惯用等效项是-> None

def foo() -> None:
    ...

这符合,如果没有一个函数return或只是一个光秃秃return的计算结果为None

def void_func():  # unannotated void function
    pass

print(void())  # None

省略返回类型也并不意味着就没有返回值。根据PEP 484

对于选中的函数,参数和返回类型的默认注释为Any

这意味着该值被认为是动态类型的,并且静态地支持任何操作。这实际上是的相反含义void


Python中的类型提示并不严格要求实际类型。例如,注释可以使用类型名称为Union[str, int],,的字符串Union[str, 'int']'Union[str, int]'并且各种变体是等效的。

同样,类型注释None被认为 “ is of NoneType”。这不仅可以用于返回类型,尽管您会经常在以下位置看到它:

bar : None

def foo(baz: None) -> None:
    return None

这也适用于泛型类型。例如,您可以使用Nonein Generator[int, None, None]指示生成器不接受也不返回值。


即使PEP 484建议采用这种None方式type(None),您也不应明确使用后者。该类型提示规范并没有包括任何形式的type(...)。从技术上讲,这是一个运行时表达式,其支持完全取决于类型检查器。该mypy项目正在考虑取消type(None)484的支持并将其也从484中删除。

或者,也许我们应该更新PEP 484以不建议该type(None)类型有效。None是唯一正确的拼写吗?应该有一种-最好只有一种-明显的方式来做等。

JukkaL,2018年5月18日

TLDR: The idiomatic equivalent of a void return type annotation is -> None.

def foo() -> None:
    ...

This matches that a function without return or just a bare return evaluates to None.

def void_func():  # unannotated void function
    pass

print(void())  # None

Omitting the return type does not mean that there is no return value. As per PEP 484:

For a checked function, the default annotation for arguments and for the return type is Any.

This means the value is considered dynamically typed and statically supports any operation. That is practically the opposite meaning of void.


Type hinting in Python does not strictly require actual types. For example, annotations may use strings of type names: Union[str, int], Union[str, 'int'], 'Union[str, int]' and various variants are equivalent.

Similarly, the type annotation None is considered to mean “is of NoneType“. This can be used not just for return types, though you will see it most often there:

bar : None

def foo(baz: None) -> None:
    return None

This also applies to generic types. For example, you can use None in Generator[int, None, None] to indicate a generator does not take or return values.


Even though PEP 484 suggests that None means type(None), you should not use the latter form explicitly. The type hinting specification does not include any form of type(...). This is technically a runtime expression, and its support is entirely up to the type checker. The mypy project is considering to remove support for type(None) and remove it from 484 as well.

Or maybe we should update PEP 484 to not suggest that type(None) is valid as a type, and None is the only correct spelling? There should one — and preferably only one — obvious way to do it etc.

JukkaL, 18 May 2018


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

问题:Python3的“函数注释”有什么好的用处

功能注释:PEP-3107

我碰到了一段代码,展示了Python3的功能注释。这个概念很简单,但是我想不起来为什么要用Python3来实现它们或对其有很好的用途。也许可以启发我吗?

这个怎么运作:

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

在参数后冒号后面的所有内容均为“注释”,在后面的信息->为函数返回值的注释。

foo.func_annotations将返回一个字典:

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

拥有此功能有什么意义?

Function Annotations: PEP-3107

I ran across a snippet of code demonstrating Python3’s function annotations. The concept is simple but I can’t think of why these were implemented in Python3 or any good uses for them. Perhaps SO can enlighten me?

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:

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

What’s the significance of having this available?


回答 0

我认为这实际上很棒。

来自学术背景,我可以告诉您,注释已证明对启用像Java这样的语言的智能静态分析器非常有用。例如,您可以定义语义,例如状态限制,允许访问的线程,体系结构限制等,然后有很多工具可以读取这些内容并进行处理,以提供超出编译器的保证。您甚至可以编写检查前提条件/后置条件的东西。

我觉得这样的事情在Python中特别需要,因为它的输入较弱,但是实际上没有任何结构可以使它简单明了,并且成为正式语法的一部分。

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

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

There are other uses for annotations beyond assurance. I can see how I could apply my Java-based tools to 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 (E.g., 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 technicall write something like this for Python. Similarly, a tool that organizes methods in a large class based on tags can be written if there is an official syntax.


回答 1

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

它们可以用于文档:

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/了解实现类型检查的方法。

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.


回答 2

这是一个较晚的答案,但是AFAICT(当前对功能注释的最佳使用)是PEP-0484MyPy

Mypy是Python的可选静态类型检查器。您可以使用即将在Python 3.5 beta 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

This is a way late answer, but AFAICT, the best current use of function annotations is PEP-0484 and MyPy.

Mypy is an optional static type checker for 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.

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

回答 3

我想补充从我的回答很好地利用的一个具体的例子在这里,加上装饰可以做的多方法的简单机制。

# 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的原始文章所示,但是对参数本身进行注释会更好,因为这样可以避免错误地匹配参数和类型。

:在Python中,你可以访问注解function.__annotations__,而不是function.func_annotations因为func_*风格是关于Python 3去除。

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.

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.


回答 4

Uri已经给出了正确的答案,所以下面是一个不太严重的答案:这样您可以缩短文档字符串。

Uri has already given a proper answer, so here’s a less serious one: So you can make your docstrings shorter.


回答 5

第一次看到注释时,我以为“很棒!最后我可以选择进行类型检查!” 当然,我没有注意到注解实际上并没有执行。

因此,我决定编写一个简单的函数装饰器来实施它们

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

我已将其添加到“ 确保”库中。

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.


回答 6

自问起以来已经有很长时间了,但问题中给出的示例摘录(也如此处所述)来自PEP 3107,并且在thas PEP示例结尾处也给出了用例,它们可能会从PEP角度回答问题。查看;)

以下引自PEP3107

用例

在讨论注释的过程中,提出了许多用例。其中一些按其传达的信息进行分组。还包括可以利用注释的现有产品和包装的示例。

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

有关特定点(及其参考)的更多信息,请参见PEP

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

The following is quoted from 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])
    • Let IDEs show what types a function expects and returns ([17])
    • Function overloading / generic functions ([22])
    • Foreign-language bridges ([18], [19])
    • Adaptation ([21], [20])
    • Predicate logic functions
    • Database query mapping
    • RPC parameter marshaling ([23])
  • Other information
    • Documentation for parameters and return values ([24])

See the PEP for more information on specific points (as well as their references)


回答 7

Python 3.X(仅)还泛化了函数定义,以允许将参数和返回值与对象值一起注释以 用于扩展

用其META数据进行解释,以更明确地了解函数值。

注释的编码方式是: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代码的类型检查和类型推断的工具。它还提供了一组用于注释功能和对象的类型。

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

https://github.com/ceronman/typeannotations

键入如何帮助编写更好的代码

键入可以帮助您进行静态代码分析,以在将代码发送到生产环境之前捕获类型错误,并防止出现一些明显的错误。有些工具例如mypy,可以将其添加到工具箱中,作为软件生命周期的一部分。mypy可以通过部分或完全针对您的代码库运行来检查类型是否正确。mypy还可以帮助您检测错误,例如从函数返回值时检查None类型。键入有助于使代码更整洁。您可以在不增加性能成本的情况下使用类型,而不必使用注释在文档字符串中指定类型的方式来记录代码。

干净的Python:Python中的优雅编码ISBN:ISBN-13(pbk):978-1-4842-4877-5

PEP 526-变量注释的语法

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

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

Python 3.X (only) also generalizes function definition to allow arguments and return values to be annotated with object values for use in extensions.

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.

They are collected into an __annotations__ attribute of the function, but are not otherwise treated as special by Python itself:

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

EXAMPLE:

The typeannotations module provides a set of tools for type checking and type inference of Python code. 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. 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.

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 can check for correct types by running against your codebase partially or fully. mypy also helps you to detect bugs such as checking for the None type when the value is returned from a function. 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

PEP 526 — Syntax for Variable Annotations

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

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


回答 8

尽管在此描述了所有用法,但注释的一种可执行且最有可能的强制使用将是类型提示

目前尚未以任何方式强制执行此操作,但从PEP 484判断,Python的未来版本将仅允许类型作为注释的值。

引用注释的现有用法如何?

我们确实希望类型提示最终将成为注释的唯一用法,但这在使用Python 3.5首次键入类型模块之后,将需要进行额外的讨论和弃用期。当前的PEP将具有临时状态(请参阅PEP 411),直到发布Python 3.6。最快的可能方案将在3.6中引入对非类型提示注释的静默弃用,在3.7中引入完全弃用,并将类型提示声明为Python 3.8中唯一允许使用的注释。

尽管我还没有在3.6中看到任何过时的贬值,但是很可能会升至3.7。

因此,即使可能还有其他一些很好的用例,如果您不想在将来有此限制的情况下四处更改所有内容,最好还是仅将它们保留为类型提示。

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.

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. The current PEP will have provisional status (see PEP 411 ) until Python 3.6 is released. 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.

Though I haven’t seen any silent deprecations in 3.6 yet, this could very well be bumped to 3.7, instead.

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.


回答 9

作为一个延迟回答的问题,我的一些软件包(marrow.script,WebCore等)也使用了注释来声明类型转换(即,转换来自Web的传入值,检测哪些参数是布尔开关等)。以执行其他参数标记。

Marrow Script可为任意函数和类构建完整的命令行界面,并允许通过注释定义文档,强制转换和回调派生的默认值,并带有装饰器以支持较早的运行时。我所有使用注释的库都支持以下形式:

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

对文档字符串或类型转换功能的“裸露”支持可简化与其他可识别注释的库的混合。(即,有一个使用类型转换的Web控制器,它也恰巧作为命令行脚本公开。)

编辑添加:我还开始使用TypeGuard包,该包使用开发时断言进行验证。好处:在启用“优化”(-O/ PYTHONOPTIMIZEenv var)的情况下运行时,可能会很昂贵(例如,递归)的检查被省略,因为您已经在开发中正确测试了应用程序,因此在生产中不必要检查。

As a bit of a delayed answer, several of my packages (marrow.script, WebCore, etc.) use annotations where available to declare typecasting (i.e. transforming incoming values from the web, detecting which arguments are boolean switches, etc.) as well as to perform additional markup of arguments.

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. 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. (I.e. have a web controller using typecasting that also happens to be exposed as a command-line script.)

Edited to add: I’ve also begun making use of the TypeGuard package using development-time assertions for validation. Benefit: when run with “optimizations” enabled (-O / PYTHONOPTIMIZE env var) the checks, which may be expensive (e.g. recursive) are omitted, with the idea that you’ve properly tested your app in development so the checks should be unnecessary in production.


回答 10

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

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

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

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

请参阅我们的函数元数据对象以获取开放源代码类,该类对此有所帮助,并且可以自动检索所需的值并将其转换为任何所需的类型(因为注释是一种转换方法)。甚至IDE都显示正确的自动完成功能,并假定类型符合注释-非常合适。

Annotations can be used for easily modularizing code. E.g. 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”. 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.


回答 11

如果您查看Cython的好处列表,那么主要的一项功能就是能够告诉编译器Python对象的类型。

我可以预见一个未来,Cython(或编译某些Python代码的类似工具)将使用注释语法来发挥作用。

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.

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.


->在Python函数定义中是什么意思?

问题:->在Python函数定义中是什么意思?

我最近在查看Python 3.3语法规范时发现了一些有趣的东西:

funcdef: 'def' NAME parameters ['->' test] ':' suite

Python 2中没有可选的“箭头”块,我在Python 3中找不到有关其含义的任何信息。事实证明这是正确的Python,并且已被解释器接受:

def f(x) -> 123:
    return x

我认为这可能是某种前提语法,但是:

  • 我无法x在此处进行测试,因为它仍未定义,
  • 无论我在箭头后面加什么(例如2 < 1),它都不会影响功能行为。

习惯此语法的任何人都可以解释吗?

I’ve recently noticed something interesting when looking at Python 3.3 grammar specification:

funcdef: 'def' NAME parameters ['->' test] ':' suite

The optional ‘arrow’ block was absent in Python 2 and I couldn’t find any information regarding its meaning in Python 3. It turns out this is correct Python and it’s accepted by the interpreter:

def f(x) -> 123:
    return x

I thought that this might be some kind of a precondition syntax, but:

  • I cannot test x here, at it is still undefined,
  • No matter what I put after the arrow (e.g. 2 < 1), it doesn’t affect the function behaviour.

Could anyone accustomed with this syntax explain it?


回答 0

这是一个功能注释

更详细地讲,Python 2.x具有文档字符串,可用于将元数据字符串附加到各种类型的对象。这非常方便,因此Python 3通过允许您将元数据附加到描述其参数和返回值的函数来扩展了该功能。

没有预想的用例,但是PEP建议了几个。一种非常方便的方法是允许您使用期望的类型注释参数。这样就很容易编写一个装饰器来验证注释或将参数强制为正确的类型。另一个是允许特定于参数的文档,而不是将其编码为文档字符串。

It’s a function annotation.

In more detail, Python 2.x has docstrings, which allow you to attach a metadata string to various types of object. This is amazingly handy, so Python 3 extends the feature by allowing you to attach metadata to functions describing their parameters and return values.

There’s no preconceived use case, but the PEP suggests several. One very handy one is to allow you to annotate parameters with their expected types; it would then be easy to write a decorator that verifies the annotations or coerces the arguments to the right type. Another is to allow parameter-specific documentation instead of encoding it into the docstring.


回答 1

这些是PEP 3107中涵盖的功能注释。具体来说,->标记是返回函数注释。

例子:

>>> def kinetic_energy(m:'in KG', v:'in M/S')->'Joules': 
...    return 1/2*m*v**2
... 
>>> kinetic_energy.__annotations__
{'return': 'Joules', 'v': 'in M/S', 'm': 'in KG'}

注释是字典,因此您可以执行以下操作:

>>> '{:,} {}'.format(kinetic_energy(20,3000),
      kinetic_energy.__annotations__['return'])
'90,000,000.0 Joules'

您还可以拥有一个python数据结构,而不仅仅是一个字符串:

>>> rd={'type':float,'units':'Joules','docstring':'Given mass and velocity returns kinetic energy in Joules'}
>>> def f()->rd:
...    pass
>>> f.__annotations__['return']['type']
<class 'float'>
>>> f.__annotations__['return']['units']
'Joules'
>>> f.__annotations__['return']['docstring']
'Given mass and velocity returns kinetic energy in Joules'

或者,您可以使用函数属性来验证调用的值:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        try: 
            pr=test.__name__+': '+test.__docstring__
        except AttributeError:
            pr=test.__name__   
        msg = '{}=={}; Test: {}'.format(var, value, pr)
        assert test(value), msg

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    _between.__docstring__='must be between {} and {}'.format(lo,hi)       
    return _between

def f(x: between(3,10), y:lambda _y: isinstance(_y,int)):
    validate(f, locals())
    print(x,y)

版画

>>> f(2,2) 
AssertionError: x==2; Test: _between: must be between 3 and 10
>>> f(3,2.1)
AssertionError: y==2.1; Test: <lambda>

These are function annotations covered in PEP 3107. Specifically, the -> marks the return function annotation.

Examples:

>>> def kinetic_energy(m:'in KG', v:'in M/S')->'Joules': 
...    return 1/2*m*v**2
... 
>>> kinetic_energy.__annotations__
{'return': 'Joules', 'v': 'in M/S', 'm': 'in KG'}

Annotations are dictionaries, so you can do this:

>>> '{:,} {}'.format(kinetic_energy(20,3000),
      kinetic_energy.__annotations__['return'])
'90,000,000.0 Joules'

You can also have a python data structure rather than just a string:

>>> rd={'type':float,'units':'Joules','docstring':'Given mass and velocity returns kinetic energy in Joules'}
>>> def f()->rd:
...    pass
>>> f.__annotations__['return']['type']
<class 'float'>
>>> f.__annotations__['return']['units']
'Joules'
>>> f.__annotations__['return']['docstring']
'Given mass and velocity returns kinetic energy in Joules'

Or, you can use function attributes to validate called values:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        try: 
            pr=test.__name__+': '+test.__docstring__
        except AttributeError:
            pr=test.__name__   
        msg = '{}=={}; Test: {}'.format(var, value, pr)
        assert test(value), msg

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    _between.__docstring__='must be between {} and {}'.format(lo,hi)       
    return _between

def f(x: between(3,10), y:lambda _y: isinstance(_y,int)):
    validate(f, locals())
    print(x,y)

Prints

>>> f(2,2) 
AssertionError: x==2; Test: _between: must be between 3 and 10
>>> f(3,2.1)
AssertionError: y==2.1; Test: <lambda>

回答 2

如其他答案所述,该->符号用作功能注释的一部分。>= 3.5但是,在最新版本的Python中,它具有定义的含义。

PEP 3107-功能注释描述了规范,定义了语法更改,func.__annotations__它们存储在其中的存在以及用例的事实仍然是开放的。

但是在Python中3.5PEP 484-类型提示对此具有唯一含义:->用于指示函数返回的类型。看起来这将在将来的版本中强制执行,如注释的现有用法如何

最快的可能方案将在3.6中引入对非类型提示注释的静默弃用,在3.7中引入完全弃用,并将类型提示声明为Python 3.8中唯一允许使用的注释。

(强调我的)

3.6据我所知,实际上尚未真正实现,因此可能会与将来的版本发生冲突。

据此,您提供了示例:

def f(x) -> 123:
    return x

将来会被禁止(并且在当前版本中会令人困惑),因此需要将其更改为:

def f(x) -> int:
    return x

为了有效地描述该函数f返回一个类型的对象int

Python本身不以任何方式使用这些注释,它几乎填充并忽略了它们。与他们合作的取决于第三方图书馆。

As other answers have stated, the -> symbol is used as part of function annotations. In more recent versions of Python >= 3.5, though, it has a defined meaning.

PEP 3107 — Function Annotations described the specification, defining the grammar changes, the existence of func.__annotations__ in which they are stored and, the fact that it’s use case is still open.

In Python 3.5 though, PEP 484 — Type Hints attaches a single meaning to this: -> is used to indicate the type that the function returns. It also seems like this will be enforced in future versions as described in What about existing uses of annotations:

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.

(Emphasis mine)

This hasn’t been actually implemented as of 3.6 as far as I can tell so it might get bumped to future versions.

According to this, the example you’ve supplied:

def f(x) -> 123:
    return x

will be forbidden in the future (and in current versions will be confusing), it would need to be changed to:

def f(x) -> int:
    return x

for it to effectively describe that function f returns an object of type int.

The annotations are not used in any way by Python itself, it pretty much populates and ignores them. It’s up to 3rd party libraries to work with them.


回答 3

在下面的代码中:

def f(x) -> int:
    return int(x)

-> int刚刚告诉f()返回一个整数(但并不强制函数返回一个整数)。它称为返回注释,可以通过进行访问f.__annotations__['return']

Python还支持参数注释:

def f(x: float) -> int:
    return int(x)

: float告诉阅读该程序(和某些第三方库/程序,例如pylint)的人员x应为float。它以形式访问f.__annotations__['x'],其本身没有任何意义。请参阅文档以获取更多信息:

https://docs.python.org/3/reference/compound_stmts.html#function-definitions https://www.python.org/dev/peps/pep-3107/

In the following code:

def f(x) -> int:
    return int(x)

the -> int just tells that f() returns an integer (but it doesn’t force the function to return an integer). It is called a return annotation, and can be accessed as f.__annotations__['return'].

Python also supports parameter annotations:

def f(x: float) -> int:
    return int(x)

: float tells people who read the program (and some third-party libraries/programs, e. g. pylint) that x should be a float. It is accessed as f.__annotations__['x'], and doesn’t have any meaning by itself. See the documentation for more information:

https://docs.python.org/3/reference/compound_stmts.html#function-definitions https://www.python.org/dev/peps/pep-3107/


回答 4

这意味着函数返回的结果类型,但可以是None

它在面向Python 3.x的现代库中很普遍。

例如,在很多地方,库pandas-profiling中都有代码,例如:

def get_description(self) -> dict:

def get_rejected_variables(self, threshold: float = 0.9) -> list:

def to_file(self, output_file: Path or str, silent: bool = True) -> None:
"""Write the report to a file.

This means the type of result the function returns, but it can be None.

It is widespread in modern libraries oriented on Python 3.x.

For example, it there is in code of library pandas-profiling in many places for example:

def get_description(self) -> dict:

def get_rejected_variables(self, threshold: float = 0.9) -> list:

def to_file(self, output_file: Path or str, silent: bool = True) -> None:
"""Write the report to a file.

回答 5

def function(arg)->123:

它只是一个返回类型,在这种情况下,整数与您写入的数字无关紧要。

Java一样:

public int function(int args){...}

但是对于Python(Jim Fasarakis Hilliard怎么说) ,返回类型只是一个提示,因此建议返回,但是无论如何都允许返回其他类型,例如字符串。

def function(arg)->123:

It’s simply a return type, integer in this case doesn’t matter which number you write.

like Java :

public int function(int args){...}

But for Python (how Jim Fasarakis Hilliard said) the return type it’s just an hint, so it’s suggest the return but allow anyway to return other type like a string..


回答 6

def f(x) -> 123:
    return x

我的总结:

  1. 简单->介绍它是为了使开发人员可以选择指定函数的返回类型。请参阅Python增强建议3107

  2. 这表明随着Python的广泛采用,事情将来会如何发展-这是强类型化的迹象-这是我个人的观察。

  3. 您也可以为参数指定类型。指定函数和参数的返回类型将有助于减少逻辑错误并改进代码增强功能。

  4. 您可以将表达式作为返回类型(对于函数和参数级别),并且可以通过注释对象的’return’属性访问表达式的结果。对于lambda内联函数的表达式/返回值,注释将为空。

def f(x) -> 123:
    return x

My summary:

  1. Simply -> is introduced to get developers to optionally specify the return type of the function. See Python Enhancement Proposal 3107

  2. This is an indication of how things may develop in future as Python is adopted extensively – an indication towards strong typing – this is my personal observation.

  3. You can specify types for arguments as well. Specifying return type of the functions and arguments will help in reducing logical errors and improving code enhancements.

  4. You can have expressions as return type (for both at function and parameter level) and the result of the expressions can be accessed via annotations object’s ‘return’ attribute. annotations will be empty for the expression/return value for lambda inline functions.


Labelme-使用Python的图像多边形批注(多边形、矩形、圆、直线、点和图像级标志批注)

Labelme是一个图形图像标注工具,灵感来自http://labelme.csail.mit.edu
它是用Python编写的,并使用Qt作为其图形界面


各种图元(多边形、矩形、圆、直线和点)

功能

要求

安装

有以下选项:

python

您需要安装Anaconda,然后在下面运行:

# python2
conda create --name=labelme python=2.7
source activate labelme
# conda install -c conda-forge pyside2
conda install pyqt
pip install labelme
# if you'd like to use the latest version. run below:
# pip install git+https://github.com/wkentaro/labelme.git

# python3
conda create --name=labelme python=3.6
source activate labelme
# conda install -c conda-forge pyside2
# conda install pyqt
# pip install pyqt5  # pyqt5 can be installed via pip on python3
pip install labelme
# or you can install everything by conda command
# conda install labelme -c conda-forge

码头工人

您需要安装docker,然后在下面运行:

# on macOS
socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" &
docker run -it -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=docker.for.mac.host.internal:0 -v $(pwd):/root/workdir wkentaro/labelme

# on Linux
xhost +
docker run -it -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=:0 -v $(pwd):/root/workdir wkentaro/labelme

Ubuntu

# Ubuntu 14.04 / Ubuntu 16.04
# Python2
# sudo apt-get install python-qt4  # PyQt4
sudo apt-get install python-pyqt5  # PyQt5
sudo pip install labelme
# Python3
sudo apt-get install python3-pyqt5  # PyQt5
sudo pip3 install labelme

# or install standalone executable from:
# https://github.com/wkentaro/labelme/releases

Ubuntu 19.10+/debian(Sid)

sudo apt-get install labelme

MacOS

# macOS Sierra
brew install pyqt  # maybe pyqt5
pip install labelme  # both python2/3 should work

# or install standalone executable/app from:
# https://github.com/wkentaro/labelme/releases

窗口

安装Anaconda,然后在python提示运行中:

# python3
conda create --name=labelme python=3.6
conda activate labelme
pip install labelme

用法

labelme --help有关详细信息,请参阅
批注另存为JSON文件

labelme  # just open gui

# tutorial (single image example)
cd examples/tutorial
labelme apc2016_obj3.jpg  # specify image file
labelme apc2016_obj3.jpg -O apc2016_obj3.json  # close window after the save
labelme apc2016_obj3.jpg --nodata  # not include image data but relative image path in JSON file
labelme apc2016_obj3.jpg \
  --labels highland_6539_self_stick_notes,mead_index_cards,kong_air_dog_squeakair_tennis_ball  # specify label list

# semantic segmentation example
cd examples/semantic_segmentation
labelme data_annotated/  # Open directory to annotate all images in it
labelme data_annotated/ --labels labels.txt  # specify label list with a file

有关更高级的用法,请参阅示例:

命令行参数

  • --output指定将写入批注的位置。如果位置以.json结尾,则此文件中将写入单个注释。如果使用.json指定位置,则只能注释一个图像。如果位置不是以.json结尾,程序将假定它是一个目录。批注将存储在此目录中,其名称与在其上进行批注的图像相对应
  • 第一次运行labelme时,它将在~/.labelmerc您可以编辑此文件,更改将在下次启动labelme时应用。如果您希望使用来自其他位置的配置文件,可以使用--config旗帜
  • 如果没有--nosortlabels标志时,程序将按字母顺序列出标签。当程序使用此标志运行时,它将按照标签提供的顺序显示标签
  • 将标志分配给整个图像。Example
  • 标签指定给单个多边形。Example

常见问题解答

测试

pip install hacking pytest pytest-qt
flake8 .
pytest -v tests

发展中的

git clone https://github.com/wkentaro/labelme.git
cd labelme

# Install anaconda3 and labelme
curl -L https://github.com/wkentaro/dotfiles/raw/master/local/bin/install_anaconda3.sh | bash -s .
source .anaconda3/bin/activate
pip install -e .

如何构建独立的可执行文件

下面显示了如何在MacOS、Linux和Windows上构建独立的可执行文件

# Setup conda
conda create --name labelme python==3.6.0
conda activate labelme

# Build the standalone executable
pip install .
pip install pyinstaller
pyinstaller labelme.spec
dist/labelme --version

如何做出贡献

确保以下测试在您的环境中通过
看见.github/workflows/ci.yml有关更多详细信息,请参阅

pip install black hacking pytest pytest-qt

flake8 .
black --line-length 79 --check labelme/
MPLBACKEND='agg' pytest tests/ -m 'not gpu'

确认

这项回购是mpitid/pylabelme,它的发育已经停止了

引用此项目

如果您在研究中使用此项目或希望参考自述文件中发布的基线结果,请使用以下BibTeX条目

@misc{labelme2016,
  author =       {Kentaro Wada},
  title =        {{labelme: Image Polygonal Annotation with Python}},
  howpublished = {\url{https://github.com/wkentaro/labelme}},
  year =         {2016}
}