标签归档:type-hinting

我怎样才能告诉PyCharm参数期望是什么类型?

问题:我怎样才能告诉PyCharm参数期望是什么类型?

当涉及到构造函数,赋值和方法调用时,PyCharm IDE非常擅长分析我的源代码并弄清楚每个变量应该是什么类型。我很喜欢它,因为它给了我很好的代码完成和参数信息,并且如果我尝试访问一个不存在的属性,它会给我警告。

但是当涉及到参数时,它一无所知。代码完成下拉列表无法显示任何内容,因为它们不知道参数的类型。代码分析无法查找警告。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

peasant = Person("Dennis", 37)
# PyCharm knows that the "peasant" variable is of type Person
peasant.dig_filth()   # shows warning -- Person doesn't have a dig_filth method

class King:
    def repress(self, peasant):
        # PyCharm has no idea what type the "peasant" parameter should be
        peasant.knock_over()   # no warning even though knock_over doesn't exist

King().repress(peasant)
# Even if I call the method once with a Person instance, PyCharm doesn't
# consider that to mean that the "peasant" parameter should always be a Person

这在一定程度上是有意义的。其他呼叫站点可以为该参数传递任何内容。但是,如果我的方法希望参数的类型为,则pygame.Surface我希望能够以某种方式向PyCharm指出,因此它可以Surface在其代码完成下拉列表中向我显示所有的属性,并在警告时突出显示警告我调用了错误的方法,依此类推。

有没有办法给PyCharm一个提示,然后说“ psst,该参数应该是X类型”?(或者,也许是本着动态语言的精神,“这个参数应该像X一样嘎嘎叫”?对此我可以接受。)


编辑:下面的CrazyCoder的答案就可以了。对于像我这样想要快速摘要的任何新手,这里是:

class King:
    def repress(self, peasant):
        """
        Exploit the workers by hanging on to outdated imperialist dogma which
        perpetuates the economic and social differences in our society.

        @type peasant: Person
        @param peasant: Person to repress.
        """
        peasant.knock_over()   # Shows a warning. And there was much rejoicing.

相关部分是@type peasant: Person文档字符串的行。

如果还转到“文件”>“设置”>“ Python集成工具”,并将“文档字符串格式”设置为“ Epytext”,则PyCharm的“视图”>“快速文档查找”将漂亮地打印参数信息,而不是仅按原样打印所有@ -lines。

When it comes to constructors, and assignments, and method calls, the PyCharm IDE is pretty good at analyzing my source code and figuring out what type each variable should be. I like it when it’s right, because it gives me good code-completion and parameter info, and it gives me warnings if I try to access an attribute that doesn’t exist.

But when it comes to parameters, it knows nothing. The code-completion dropdowns can’t show anything, because they don’t know what type the parameter will be. The code analysis can’t look for warnings.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

peasant = Person("Dennis", 37)
# PyCharm knows that the "peasant" variable is of type Person
peasant.dig_filth()   # shows warning -- Person doesn't have a dig_filth method

class King:
    def repress(self, peasant):
        # PyCharm has no idea what type the "peasant" parameter should be
        peasant.knock_over()   # no warning even though knock_over doesn't exist

King().repress(peasant)
# Even if I call the method once with a Person instance, PyCharm doesn't
# consider that to mean that the "peasant" parameter should always be a Person

This makes a certain amount of sense. Other call sites could pass anything for that parameter. But if my method expects a parameter to be of type, say, pygame.Surface, I’d like to be able to indicate that to PyCharm somehow, so it can show me all of Surface‘s attributes in its code-completion dropdown, and highlight warnings if I call the wrong method, and so on.

Is there a way I can give PyCharm a hint, and say “psst, this parameter is supposed to be of type X”? (Or perhaps, in the spirit of dynamic languages, “this parameter is supposed to quack like an X”? I’d be fine with that.)


EDIT: CrazyCoder’s answer, below, does the trick. For any newcomers like me who want the quick summary, here it is:

class King:
    def repress(self, peasant):
        """
        Exploit the workers by hanging on to outdated imperialist dogma which
        perpetuates the economic and social differences in our society.

        @type peasant: Person
        @param peasant: Person to repress.
        """
        peasant.knock_over()   # Shows a warning. And there was much rejoicing.

The relevant part is the @type peasant: Person line of the docstring.

If you also go to File > Settings > Python Integrated Tools and set “Docstring format” to “Epytext”, then PyCharm’s View > Quick Documentation Lookup will pretty-print the parameter information instead of just printing all the @-lines as-is.


回答 0

是的,您可以对方法及其参数使用特殊的文档格式,以便PyCharm可以知道类型。最新的PyCharm版本支持大多数常见的doc格式

例如,PyCharm从@param样式注释中提取类型。

另请参见reStructuredTextdocstring约定(PEP 257)。

另一个选择是Python 3注释。

参阅PyCharm文档部分以获取更多详细信息和示例。

Yes, you can use special documentation format for methods and their parameters so that PyCharm can know the type. Recent PyCharm version supports most common doc formats.

For example, PyCharm extracts types from @param style comments.

See also reStructuredText and docstring conventions (PEP 257).

Another option is Python 3 annotations.

Please refer to the PyCharm documentation section for more details and samples.


回答 1

如果您使用的是Python 3.0或更高版本,则还可以在函数和参数上使用注释。PyCharm会将这些解释为参数或返回值应具有的类型:

class King:
    def repress(self, peasant: Person) -> bool:
        peasant.knock_over() # Shows a warning. And there was much rejoicing.

        return peasant.badly_hurt() # Lets say, its not known from here that this method will always return a bool

有时,这对于不需要文档字符串的非公共方法很有用。另外一个好处是,这些注释可以通过代码访问:

>>> King.repress.__annotations__
{'peasant': <class '__main__.Person'>, 'return': <class 'bool'>}

更新:从PEP 484(已为Python 3.5接受)开始,使用注释指定参数和返回类型也是官方约定。

If you are using Python 3.0 or later, you can also use annotations on functions and parameters. PyCharm will interpret these as the type the arguments or return values are expected to have:

class King:
    def repress(self, peasant: Person) -> bool:
        peasant.knock_over() # Shows a warning. And there was much rejoicing.

        return peasant.badly_hurt() # Lets say, its not known from here that this method will always return a bool

Sometimes this is useful for non-public methods, that do not need a docstring. As an added benefit, those annotations can be accessed by code:

>>> King.repress.__annotations__
{'peasant': <class '__main__.Person'>, 'return': <class 'bool'>}

Update: As of PEP 484, which has been accepted for Python 3.5, it is also the official convention to specify argument and return types using annotations.


回答 2

PyCharm从@type pydoc字符串中提取类型。在此处此处查看PyCharm文档,以及Epydoc文档。它位于PyCharm的“旧版”部分,也许缺少某些功能。

class King:
    def repress(self, peasant):
        """
        Exploit the workers by hanging on to outdated imperialist dogma which
        perpetuates the economic and social differences in our society.

        @type peasant: Person
        @param peasant: Person to repress.
        """
        peasant.knock_over()   # Shows a warning. And there was much rejoicing.

相关部分是@type peasant: Person文档字符串的行。

我的目的不是要从CrazyCoder或原始提问者那里窃取分数,而应尽一切可能给予分数。我只是以为简单的答案应该在“答案”栏中。

PyCharm extracts types from a @type pydoc string. See PyCharm docs here and here, and Epydoc docs. It’s in the ‘legacy’ section of PyCharm, perhaps it lacks some functionality.

class King:
    def repress(self, peasant):
        """
        Exploit the workers by hanging on to outdated imperialist dogma which
        perpetuates the economic and social differences in our society.

        @type peasant: Person
        @param peasant: Person to repress.
        """
        peasant.knock_over()   # Shows a warning. And there was much rejoicing.

The relevant part is the @type peasant: Person line of the docstring.

My intention is not to steal points from CrazyCoder or the original questioner, by all means give them their points. I just thought the simple answer should be in an ‘answer’ slot.


回答 3

我正在使用PyCharm Professional 2016.1编写py2.6-2.7代码,发现使用reStructuredText可以以更简洁的方式表达类型:

class Replicant(object):
    pass


class Hunter(object):
    def retire(self, replicant):
        """ Retire the rogue or non-functional replicant.
        :param Replicant replicant: the replicant to retire.
        """
        replicant.knock_over()  # Shows a warning.

参见:https : //www.jetbrains.com/help/pycharm/2016.1/type-hinting-in-pycharm.html#legacy

I’m using PyCharm Professional 2016.1 writing py2.6-2.7 code, and I found that using reStructuredText I can express types in a more succint way:

class Replicant(object):
    pass


class Hunter(object):
    def retire(self, replicant):
        """ Retire the rogue or non-functional replicant.
        :param Replicant replicant: the replicant to retire.
        """
        replicant.knock_over()  # Shows a warning.

See: https://www.jetbrains.com/help/pycharm/2016.1/type-hinting-in-pycharm.html#legacy


回答 4

您还可以断言一个类型,Pycharm会推断出它:

def my_function(an_int):
    assert isinstance(an_int, int)
    # Pycharm now knows that an_int is of type int
    pass

You can also assert for a type and Pycharm will infer it:

def my_function(an_int):
    assert isinstance(an_int, int)
    # Pycharm now knows that an_int is of type int
    pass

如何在类型提示中指定函数类型?

问题:如何在类型提示中指定函数类型?

我想在当前的Python 3.5项目中使用类型提示。我的函数应该接收一个函数作为参数。

如何在类型提示中指定类型函数?

import typing

def my_function(name:typing.AnyStr, func: typing.Function) -> None:
    # However, typing.Function does not exist.
    # How can I specify the type function for the parameter `func`?

    # do some processing
    pass

我检查了PEP 483,但在那里找不到函数类型提示。

I want to use type hints in my current Python 3.5 project. My function should receive a function as parameter.

How can I specify the type function in my type hints?

import typing

def my_function(name:typing.AnyStr, func: typing.Function) -> None:
    # However, typing.Function does not exist.
    # How can I specify the type function for the parameter `func`?

    # do some processing
    pass

I checked PEP 483, but could not find a function type hint there.


回答 0

正如@jonrsharpe在评论中指出的,可以使用以下方法完成typing.Callable

from typing import AnyStr, Callable

def my_function(name: AnyStr, func: Callable) -> None:

问题是,Callable将其本身翻译为Callable[..., Any]

一个Callable接受任意数量的/类型的参数,并返回任何类型的值。在大多数情况下,这不是您想要的,因为您几乎可以允许传递任何函数。您也希望提示函数参数和返回类型。

这就是为什么许多typesin typing重载以支持表示这些额外类型的子脚本的原因。因此,例如,如果您有一个函数sum接受两个ints并返回一个int

def sum(a: int, b: int) -> int: return a+b

您的注释为:

Callable[[int, int], int]

也就是说,参数在外部订阅中带有下标,返回类型作为外部订阅中的第二个元素。一般来说:

Callable[[ParamType1, ParamType2, .., ParamTypeN], ReturnType]

As @jonrsharpe noted in a comment, this can be done with typing.Callable:

from typing import AnyStr, Callable

def my_function(name: AnyStr, func: Callable) -> None:

Issue is, Callable on it’s own is translated to Callable[..., Any] which means:

A callable takes any number of/type of arguments and returns a value of any type. In most cases, this isn’t what you want since you’ll allow pretty much any function to be passed. You want the function parameters and return types to be hinted too.

That’s why many types in typing have been overloaded to support sub-scripting which denotes these extra types. So if, for example, you had a function sum that takes two ints and returns an int:

def sum(a: int, b: int) -> int: return a+b

Your annotation for it would be:

Callable[[int, int], int]

that is, the parameters are sub-scripted in the outer subscription with the return type as the second element in the outer subscription. In general:

Callable[[ParamType1, ParamType2, .., ParamTypeN], ReturnType]

回答 1

另一个需要注意的有趣点是,您可以使用内置函数type()来获取内置函数的类型并使用它。所以你可以

def f(my_function: type(abs)) -> int:
    return my_function(100)

或那种形式的东西

Another interesting point to note is that you can use the built in function type() to get the type of a built in function and use that. So you could have

def f(my_function: type(abs)) -> int:
    return my_function(100)

Or something of that form


类型提示指定类型的列表

问题:类型提示指定类型的列表

使用Python 3的功能注释,可以指定包含在同类列表(或其他集合)中的项的类型,以便在PyCharm和其他IDE中进行类型提示。

一个int列表的伪python代码示例:

def my_func(l:list<int>):
    pass

我知道有可能使用Docstring …

def my_func(l):
    """
    :type l: list[int]
    """
    pass

…但是我更喜欢注释样式。

Using Python 3’s function annotations, it is possible to specify the type of items contained within a homogeneous list (or other collection) for the purpose of type hinting in PyCharm and other IDEs?

A pseudo-python code example for a list of int:

def my_func(l:list<int>):
    pass

I know it’s possible using Docstring…

def my_func(l):
    """
    :type l: list[int]
    """
    pass

… but I prefer the annotation style if it’s possible.


回答 0

回答我自己的问题;TLDR的答案是“

更新2

在2015年9月,Python 3.5发行了对Type Hints的支持,并包含一个新的打字模块。这允许指定集合中包含的类型。截至2015年11月,JetBrains PyCharm 5.0完全支持Python 3.5包含类型提示,如下所示。

更新1

截至2015年5月,PEP0484(类型提示)已被正式接受。草案的实现也可以在github的ambv / typehinting下找到

原始答案

自2014年8月起,我已经确认无法使用Python 3类型注释在集合中指定类型(例如:字符串列表)。

诸如reStructuredText或Sphinx之类的格式化文档字符串的使用是可行的替代方法,并且受各种IDE支持。

看来,Guido还本着mypy的精神考虑了扩展类型注释的想法:http ://mail.python.org/pipermail/python-ideas/2014-August/028618.html

Answering my own question; the TLDR answer is No Yes.

Update 2

In September 2015, Python 3.5 was released with support for Type Hints and includes a new typing module. This allows for the specification of types contained within collections. As of November 2015, JetBrains PyCharm 5.0 fully supports Python 3.5 to include Type Hints as illustrated below.

Update 1

As of May 2015, PEP0484 (Type Hints) has been formally accepted. The draft implementation is also available at github under ambv/typehinting.

Original Answer

As of Aug 2014, I have confirmed that it is not possible to use Python 3 type annotations to specify types within collections (ex: a list of strings).

The use of formatted docstrings such as reStructuredText or Sphinx are viable alternatives and supported by various IDEs.

It also appears that Guido is mulling over the idea of extending type annotations in the spirit of mypy: http://mail.python.org/pipermail/python-ideas/2014-August/028618.html


回答 1

现在Python 3.5正式发布了,这里有Type Hints支持模块- typing以及相关的List通用容器 “类型”。

换句话说,现在您可以执行以下操作:

from typing import List

def my_func(l: List[int]):
    pass

Now that Python 3.5 is officially out, there is the Type Hints supporting module – typing and the relevant List “type” for the generic containers.

In other words, now you can do:

from typing import List

def my_func(l: List[int]):
    pass

回答 2

PEP 484起已添加类型注释

from . import Monitor
from typing import List, Set, Tuple, Dict


active_monitors = [] # type: List[Monitor]
# or
active_monitors: List[Monitor] = []

# bonus
active_monitors: Set[Monitor] = set()
monitor_pair: Tuple[Monitor, Monitor] = (Monitor(), Monitor())
monitor_dict: Dict[str, Monitor] = {'codename': Monitor()}

# nested
monitor_pair_list: List[Dict[str, Monitor]] = [{'codename': Monitor()}]

目前,这对于使用Python 3.6.4的PyCharm来说是有用的

Pycharm中的示例图片

Type comments have been added since PEP 484

from . import Monitor
from typing import List, Set, Tuple, Dict


active_monitors = [] # type: List[Monitor]
# or
active_monitors: List[Monitor] = []

# bonus
active_monitors: Set[Monitor] = set()
monitor_pair: Tuple[Monitor, Monitor] = (Monitor(), Monitor())
monitor_dict: Dict[str, Monitor] = {'codename': Monitor()}

# nested
monitor_pair_list: List[Dict[str, Monitor]] = [{'codename': Monitor()}]

This is currently working for me on PyCharm with Python 3.6.4

Example Picture in Pycharm


回答 3

在BDFL的支持下,现在几乎可以确定python(可能是3.5)将通过函数注释为类型提示提供标准化的语法。

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

正如PEP中所引用的那样,有一个名为mypy的实验型类型检查器(有点像pylint,但对于类型而言)已经使用此标准,并且不需要任何新语法。

http://mypy-lang.org/

With support from the BDFL, it’s almost certain now that python (probably 3.5) will provide a standardized syntax for type hints via function annotations.

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

As referenced in the PEP, there is an experimental type-checker (kind of like pylint, but for types) called mypy that already uses this standard, and doesn’t require any new syntax.

http://mypy-lang.org/


回答 4

从Python 3.9开始,内置类型在类型注释方面是通用的(请参阅PEP 585)。这允许直接指定元素的类型:

def my_func(l: list[int]):
    pass

各种工具可能早于Python 3.9支持此语法。如果在运行时未检查注释,则使用引号或语法有效__future__.annotations

# quoted
def my_func(l: 'list[int]'):
    pass
# postponed evaluation of annotation
from __future__ import annotations

def my_func(l: list[int]):
    pass

As of Python 3.9, builtin types are generic with respect to type annotations (see PEP 585). This allows to directly specify the type of elements:

def my_func(l: list[int]):
    pass

Various tools may support this syntax earlier than Python 3.9. When annotations are not inspected at runtime, the syntax is valid using quoting or __future__.annotations.

# quoted
def my_func(l: 'list[int]'):
    pass
# postponed evaluation of annotation
from __future__ import annotations

def my_func(l: list[int]):
    pass

在namedtuple中输入提示

问题:在namedtuple中输入提示

考虑以下代码:

from collections import namedtuple
point = namedtuple("Point", ("x:int", "y:int"))

上面的代码只是演示我正在尝试实现的方法。我想namedtuple使用类型提示。

您知道如何以一种优雅的方式达到预期效果吗?

Consider following piece of code:

from collections import namedtuple
point = namedtuple("Point", ("x:int", "y:int"))

The Code above is just a way to demonstrate as to what I am trying to achieve. I would like to make namedtuple with type hints.

Do you know any elegant way how to achieve result as intended?


回答 0

从3.6开始,类型为命名元组的首选语法为

from typing import NamedTuple

class Point(NamedTuple):
    x: int
    y: int = 1  # Set default value

Point(3)  # -> Point(x=3, y=1)

编辑 从Python 3.7开始,请考虑使用dataclasses(您的IDE可能尚不支持它们进行静态类型检查):

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int = 1  # Set default value

Point(3)  # -> Point(x=3, y=1)

The prefered Syntax for a typed named tuple since 3.6 is

from typing import NamedTuple

class Point(NamedTuple):
    x: int
    y: int = 1  # Set default value

Point(3)  # -> Point(x=3, y=1)

Edit Starting Python 3.7, consider using dataclasses (your IDE may not yet support them for static type checking):

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int = 1  # Set default value

Point(3)  # -> Point(x=3, y=1)

回答 1

您可以使用 typing.NamedTuple

来自文档

类型版本namedtuple

>>> import typing
>>> Point = typing.NamedTuple("Point", [('x', int), ('y', int)])

仅在Python 3.5及更高版本中存在

You can use typing.NamedTuple

From the docs

Typed version of namedtuple.

>>> import typing
>>> Point = typing.NamedTuple("Point", [('x', int), ('y', int)])

This is present only in Python 3.5 onwards


如何注释多个返回值的类型?

问题:如何注释多个返回值的类型?

我如何使用类型提示来注释一个返回Iterable总是返回两个值的函数:abool和a str?提示Tuple[bool, str]很接近,除了将返回值类型限制为元组,而不是生成器或其他可迭代类型。

我主要是好奇的,因为我想注释一个foo()用于返回多个值的函数,如下所示:

always_a_bool, always_a_str = foo()

通常函数喜欢foo()做这样的事情return a, b(它返回一个元组),但我喜欢的类型暗示要足够灵活,以取代生成器或列表或别的东西返回的元组。

How do I use type hints to annotate a function that returns an Iterable that always yields two values: a bool and a str? The hint Tuple[bool, str] is close, except that it limits the return value type to a tuple, not a generator or other type of iterable.

I’m mostly curious because I would like to annotate a function foo() that is used to return multiple values like this:

always_a_bool, always_a_str = foo()

Usually functions like foo() do something like return a, b (which returns a tuple), but I would like the type hint to be flexible enough to replace the returned tuple with a generator or list or something else.


回答 0

您总是返回一个对象;使用return one, two只需返回一个元组。

是的,-> Tuple[bool, str]完全正确。

只有Tuple类型可以指定一个固定数量的元素,每一个不同的类型。如果您的函数产生固定数量的返回值,尤其是当这些值是特定的,不同的类型时,确实应该总是返回一个元组。

期望其他序列类型具有可变数量元素的一种类型规范,因此typing.Sequence此处不适用。另请参阅列表和元组之间有什么区别?

元组是异构数据结构(即,它们的条目具有不同的含义),而列表是同类序列。元组具有结构,列表具有顺序。

Python的类型提示系统遵循这一理念,目前尚无语法来指定固定长度的可迭代对象,并在特定位置包含特定类型。

如果必须指定任何可迭代的对象,那么最好的方法是:

-> Iterable[Union[bool, str]]

在这一点上,调用者可以期望布尔值和字符串以任意顺序,并且长度未知(0到无穷大之间)。

You are always returning one object; using return one, two simply returns a tuple.

So yes, -> Tuple[bool, str] is entirely correct.

Only the Tuple type lets you specify a fixed number of elements, each with a distinct type. You really should be returning a tuple, always, if your function produces a fixed number of return values, especially when those values are specific, distinct types.

Other sequence types are expected to have one type specification for a variable number of elements, so typing.Sequence is not suitable here. Also see What’s the difference between lists and tuples?

Tuples are heterogeneous data structures (i.e., their entries have different meanings), while lists are homogeneous sequences. Tuples have structure, lists have order.

Python’s type hint system adheres to that philosophy, there is currently no syntax to specify an iterable of fixed length and containing specific types at specific positions.

If you must specify that any iterable will do, then the best you can do is:

-> Iterable[Union[bool, str]]

at which point the caller can expect booleans and strings in any order, and of unknown length (anywhere between 0 and infinity).


没有循环导入的Python类型提示

问题:没有循环导入的Python类型提示

我正试图将我的大班分成两部分;好吧,基本上是进入“主”类和具有其他功能的mixin的,就像这样:

main.py 文件:

import mymixin.py

class Main(object, MyMixin):
    def func1(self, xxx):
        ...

mymixin.py 文件:

class MyMixin(object):
    def func2(self: Main, xxx):  # <--- note the type hint
        ...

现在,尽管这很好用,但是类型提示MyMixin.func2当然不起作用。我无法导入main.py,因为会进行周期性导入,并且没有提示,我的编辑器(PyCharm)无法分辨出什么self

我使用的是Python 3.4,如果在那里有解决方案,我愿意移至3.5。

有什么办法可以将我的Class分成两个文件并保留所有“连接”,以便我的IDE仍然可以自动完成以及知道类型的所有其他优点。

I’m trying to split my huge class into two; well, basically into the “main” class and a mixin with additional functions, like so:

main.py file:

import mymixin.py

class Main(object, MyMixin):
    def func1(self, xxx):
        ...

mymixin.py file:

class MyMixin(object):
    def func2(self: Main, xxx):  # <--- note the type hint
        ...

Now, while this works just fine, the type hint in MyMixin.func2 of course can’t work. I can’t import main.py, because I’d get a cyclic import and without the hint, my editor (PyCharm) can’t tell what self is.

I’m using Python 3.4, willing to move to 3.5 if a solution is available there.

Is there any way I can split my class into two files and keep all the “connections” so that my IDE still offers me auto completion & all the other goodies that come from it knowing the types?


回答 0

恐怕通常没有一种非常优雅的方式来处理导入周期。您的选择是重新设计代码以消除循环依赖性,或者如果不可行,请执行以下操作:

# some_file.py

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from main import Main

class MyObject(object):
    def func2(self, some_param: 'Main'):
        ...

TYPE_CHECKING常量始终False在运行时运行,因此不会评估导入,但是mypy(和其他类型检查工具)将评估该块的内容。

我们还需要将Main类型注释放入字符串中,以有效地向前声明它,因为该Main符号在运行时不可用。

如果您使用的是Python 3.7+,我们至少可以通过利用PEP 563来跳过必须提供显式字符串注释的情况:

# some_file.py

from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from main import Main

class MyObject(object):
    # Hooray, cleaner annotations!
    def func2(self, some_param: Main):
        ...

from __future__ import annotations进口将使所有类型提示弦而跳过评估他们。这可以使我们的代码更符合人体工程学。

综上所述,与mypy一起使用mixins可能需要比您现在拥有的结构更多的结构。Mypy 建议一种基本上就是deceze所描述的方法-创建一个ABC,您的类MainMyMixin类都继承。如果您最终需要做一些类似的事情以使Pycharm的检查器满意,我不会感到惊讶。

There isn’t a hugely elegant way to handle import cycles in general, I’m afraid. Your choices are to either redesign your code to remove the cyclic dependency, or if it isn’t feasible, do something like this:

# some_file.py

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from main import Main

class MyObject(object):
    def func2(self, some_param: 'Main'):
        ...

The TYPE_CHECKING constant is always False at runtime, so the import won’t be evaluated, but mypy (and other type-checking tools) will evaluate the contents of that block.

We also need to make the Main type annotation into a string, effectively forward declaring it since the Main symbol isn’t available at runtime.

If you are using Python 3.7+, we can at least skip having to provide an explicit string annotation by taking advantage of PEP 563:

# some_file.py

from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from main import Main

class MyObject(object):
    # Hooray, cleaner annotations!
    def func2(self, some_param: Main):
        ...

The from __future__ import annotations import will make all type hints be strings and skip evaluating them. This can help make our code here mildly more ergonomic.

All that said, using mixins with mypy will likely require a bit more structure then you currently have. Mypy recommends an approach that’s basically what deceze is describing — to create an ABC that both your Main and MyMixin classes inherit. I wouldn’t be surprised if you ended up needing to do something similar in order to make Pycharm’s checker happy.


回答 1

对于仅在导入类以进行类型检查时陷入困境的人们:您可能希望使用前向引用(PEP 484-类型提示):

当类型提示包含尚未定义的名称时,该定义可以表示为字符串文字,以便稍后解析。

所以代替:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

你做:

class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right

For people struggling with cyclic imports when importing class only for Type checking: you will likely want to use a Forward Reference (PEP 484 – Type Hints):

When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.

So instead of:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

you do:

class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right

回答 2

更大的问题是,您的类型一开始并不理智。MyMixin进行硬编码的假设是将其混合到中Main,而可以将其混合到任何其他数量的类中,在这种情况下,它可能会损坏。如果将mixin硬编码为混合到一个特定的类中,则不妨将方法直接写入该类中,而不用将它们分开。

要使用合理的输入方式正确执行此操作,MyMixin应使用Python的说法对interface或abstract class 进行编码:

import abc


class MixinDependencyInterface(abc.ABC):
    @abc.abstractmethod
    def foo(self):
        pass


class MyMixin:
    def func2(self: MixinDependencyInterface, xxx):
        self.foo()  # ← mixin only depends on the interface


class Main(MixinDependencyInterface, MyMixin):
    def foo(self):
        print('bar')

The bigger issue is that your types aren’t sane to begin with. MyMixin makes a hardcoded assumption that it will be mixed into Main, whereas it could be mixed into any number of other classes, in which case it would probably break. If your mixin is hardcoded to be mixed into one specific class, you may as well write the methods directly into that class instead of separating them out.

To properly do this with sane typing, MyMixin should be coded against an interface, or abstract class in Python parlance:

import abc


class MixinDependencyInterface(abc.ABC):
    @abc.abstractmethod
    def foo(self):
        pass


class MyMixin:
    def func2(self: MixinDependencyInterface, xxx):
        self.foo()  # ← mixin only depends on the interface


class Main(MixinDependencyInterface, MyMixin):
    def foo(self):
        print('bar')

回答 3

事实证明,我最初的尝试也非常接近解决方案。这是我目前正在使用的:

# main.py
import mymixin.py

class Main(object, MyMixin):
    def func1(self, xxx):
        ...


# mymixin.py
if False:
    from main import Main

class MyMixin(object):
    def func2(self: 'Main', xxx):  # <--- note the type hint
        ...

请注意,import inside if False语句永远不会被导入(但IDE仍然知道它),并且将该Main类用作字符串,因为在运行时不知道。

Turns out my original attempt was quite close to the solution as well. This is what I’m currently using:

# main.py
import mymixin.py

class Main(object, MyMixin):
    def func1(self, xxx):
        ...


# mymixin.py
if False:
    from main import Main

class MyMixin(object):
    def func2(self: 'Main', xxx):  # <--- note the type hint
        ...

Note the import within if False statement that never gets imported (but IDE knows about it anyway) and using the Main class as string because it’s not known at runtime.


回答 4

我认为,完美的方法应该是将所有类和依赖项导入文件(如__init__.py),然后再导入所有from __init__ import *其他文件。

在这种情况下

  1. 避免对这些文件和类的多次引用,并且
  2. 也只需在其他每个文件中添加一行
  3. 第三个是知道您可能使用的所有类的pycharm。

I think the perfect way should be to import all the classes and dependencies in a file (like __init__.py) and then from __init__ import * in all the other files.

In this case you are

  1. avoiding multiple references to those files and classes and
  2. also only have to add one line in each of the other files and
  3. the third would be the pycharm knowing about all of the classes that you might use.

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


为文件或类似文件的对象键入提示?

问题:为文件或类似文件的对象键入提示?

是否有任何正确的类型提示可用于Python中的文件或类似文件的对象?例如,如何键入此函数的返回值?

def foo():
    return open('bar')

Is there any correct type hint to use for a file or file-like object in Python? For example, how would I type-hint the return value of this function?

def foo():
    return open('bar')

回答 0

对于分别以文本模式或二进制模式打开的文件,请使用typing.TextIOtyping.BinaryIO类型。

文档

typing.IO

I / O流类型的包装器命名空间。

这定义了通用类型IO[AnyStr]和别名TextIOBinaryIO用于分别IO[str]IO[bytes]。这些代表I / O流的类型,例如由返回open()

Use either the typing.TextIO or typing.BinaryIO types, for files opened in text mode or binary mode respectively.

From the docs:

class typing.IO

Wrapper namespace for I/O stream types.

This defines the generic type IO[AnyStr] and aliases TextIO and BinaryIO for respectively IO[str] and IO[bytes]. These representing the types of I/O streams such as returned by open().


回答 1

简短的答案:

  • 您需要明确。那from typing import TextIO不只是from typing import *
  • 使用IO意味着文件没有指定什么样的
  • 使用TextIOBinaryIO如果您知道类型
  • 您目前无法指定将其打开以进行写入或对其进行编码。

举个例子:

from typing import BinaryIO

def binf(inf: BinaryIO):
    pass

with open('x') as f:
    binf(f)

给出(PyCharm)的检查错误 Expected type 'BinaryIO', got 'TextIO' instead

The short answer:

  • You need to be explicit. That is from typing import TextIO not just from typing import *.
  • Use IO to mean a file without specifying what kind
  • Use TextIO or BinaryIO if you know the type
  • You cannot currently specify it be opened for write or its encoding.

As an example:

from typing import BinaryIO

def binf(inf: BinaryIO):
    pass

with open('x') as f:
    binf(f)

gives an inspection error (in PyCharm) of Expected type 'BinaryIO', got 'TextIO' instead


定义type.Dict和dict之间的区别?

问题:定义type.Dict和dict之间的区别?

我正在练习在Python 3.5中使用类型提示。我的一位同事使用typing.Dict

import typing


def change_bandwidths(new_bandwidths: typing.Dict,
                      user_id: int,
                      user_name: str) -> bool:
    print(new_bandwidths, user_id, user_name)
    return False


def my_change_bandwidths(new_bandwidths: dict,
                         user_id: int,
                         user_name: str) ->bool:
    print(new_bandwidths, user_id, user_name)
    return True


def main():
    my_id, my_name = 23, "Tiras"
    simple_dict = {"Hello": "Moon"}
    change_bandwidths(simple_dict, my_id, my_name)
    new_dict = {"new": "energy source"}
    my_change_bandwidths(new_dict, my_id, my_name)

if __name__ == "__main__":
    main()

他们两个都工作得很好,似乎没有什么区别。

我已经阅读了typing模块文档

之间typing.Dictdict哪一个,我应该在程序中使用?

I am practicing using type hints in Python 3.5. One of my colleague uses typing.Dict:

import typing


def change_bandwidths(new_bandwidths: typing.Dict,
                      user_id: int,
                      user_name: str) -> bool:
    print(new_bandwidths, user_id, user_name)
    return False


def my_change_bandwidths(new_bandwidths: dict,
                         user_id: int,
                         user_name: str) ->bool:
    print(new_bandwidths, user_id, user_name)
    return True


def main():
    my_id, my_name = 23, "Tiras"
    simple_dict = {"Hello": "Moon"}
    change_bandwidths(simple_dict, my_id, my_name)
    new_dict = {"new": "energy source"}
    my_change_bandwidths(new_dict, my_id, my_name)

if __name__ == "__main__":
    main()

Both of them work just fine, there doesn’t appear to be a difference.

I have read the typing module documentation.

Between typing.Dict or dict which one should I use in the program?


回答 0

使用Plaintyping.DictdictNo之间没有真正的区别。

然而,typing.Dict是一种通用型 *,可让您指定键和值的类型太多,使之更加灵活:

def change_bandwidths(new_bandwidths: typing.Dict[str, str],
                      user_id: int,
                      user_name: str) -> bool:

因此,很可能是在项目生命周期中的某个时候,您希望更精确地定义字典参数,在这一点上,扩展typing.Dicttyping.Dict[key_type, value_type]而不是替换是“更小的”改变dict

您可以通过在此处使用MappingMutableMapping类型来使其更通用。由于您的函数不需要更改映射,因此我坚持使用Mapping。Adict是一个映射,但是您可以创建也满足映射接口的其他对象,并且您的函数可能仍可以与那些对象一起使用:

def change_bandwidths(new_bandwidths: typing.Mapping[str, str],
                      user_id: int,
                      user_name: str) -> bool:

现在,您清楚地告诉此功能的其他用户,您的代码实际上不会更改new_bandwidths传入的映射。

您的实际实现只是期望一个可打印的对象。那可能是一个测试实现,但是就目前而言,如果使用new_bandwidths: typing.Any,您的代码将继续工作,因为Python中的任何对象都是可打印的。


*:注意:如果您使用的是Python 3.7或更高版本,则使用dict开头的模块即可将其用作通用类型from __future__ import annotations,而从python 3.9开始,dict(以及其他标准容器)也支持将其用作通用类型。指令

There is no real difference between using a plain typing.Dict and dict, no.

However, typing.Dict is a Generic type that lets you specify the type of the keys and values too, making it more flexible:

def change_bandwidths(new_bandwidths: typing.Dict[str, str],
                      user_id: int,
                      user_name: str) -> bool:

As such, it could well be that at some point in your project lifetime you want to define the dictionary argument a little more precisely, at which point expanding typing.Dict to typing.Dict[key_type, value_type] is a ‘smaller’ change than replacing dict.

You can make this even more generic by using Mapping or MutableMapping types here; since your function doesn’t need to alter the mapping, I’d stick with Mapping. A dict is one mapping, but you could create other objects that also satisfy the mapping interface, and your function might well still work with those:

def change_bandwidths(new_bandwidths: typing.Mapping[str, str],
                      user_id: int,
                      user_name: str) -> bool:

Now you are clearly telling other users of this function that your code won’t actually alter the new_bandwidths mapping passed in.

Your actual implementation is merely expecting an object that is printable. That may be a test implementation, but as it stands your code would continue to work if you used new_bandwidths: typing.Any, because any object in Python is printable.


回答 1

typing.Dict是以下内容的通用版本dict

class typing.Dict(dict, MutableMapping[KT, VT])

dict的通用版本。此类型的用法如下:

def get_position_in_index(word_list: Dict[str, int], word: str) -> int:
     return word_list[word]

在这里,您可以在dict中指定键和值的类型: Dict[str, int]

typing.Dict is a generic version of dict:

class typing.Dict(dict, MutableMapping[KT, VT])

A generic version of dict. The usage of this type is as follows:

def get_position_in_index(word_list: Dict[str, int], word: str) -> int:
     return word_list[word]

Here you can specify the type of key and values in the dict: Dict[str, int]


用户定义类的类型提示

问题:用户定义类的类型提示

似乎找不到确切的答案。我想为一个函数提供类型提示,该类型是我定义的一些自定义类,称为它CustomClass()

然后让我们说在某个函数中调用它FuncA(arg),我有一个名为的参数arg。键入提示的正确方法FuncA是:

def FuncA(arg: CustomClass):

或者是:

def FuncA(Arg:Type[CustomClass]):

Couldn’t seem to find a definitive answer. I want to do a type hint for a function and the type being some custom class that I have defined, called it CustomClass().

And then let’s say in some function, call it FuncA(arg), I have one argument named arg. Would the correct way to type hint FuncA be:

def FuncA(arg: CustomClass):

Or would it be:

def FuncA(Arg:Type[CustomClass]):?


回答 0

前者是正确的,如果arg接受一个实例CustomClass

def FuncA(arg: CustomClass):
    #     ^ instance of CustomClass

如果您想要CustomClass本身(或子类型),则应编写:

from typing import Type  # you have to import Type

def FuncA(arg: Type[CustomClass]):
    #     ^ CustomClass (class object) itself

就像在有关打字的文档中写的那样:

class typing.Type(Generic[CT_co])

带注释的变量C可以接受type的值C。相反,带注释Type[C]的变量可以接受本身是类的值 -具体地说,它将接受的类对象C

该文档包含有关int该类的示例:

a = 3         # Has type 'int'
b = int       # Has type 'Type[int]'
c = type(a)   # Also has type 'Type[int]'

The former is correct, if arg accepts an instance of CustomClass:

def FuncA(arg: CustomClass):
    #     ^ instance of CustomClass

In case you want the class CustomClass itself (or a subtype), then you should write:

from typing import Type  # you have to import Type

def FuncA(arg: Type[CustomClass]):
    #     ^ CustomClass (class object) itself

Like it is written in the documentation about Typing:

class typing.Type(Generic[CT_co])

A variable annotated with C may accept a value of type C. In contrast, a variable annotated with Type[C] may accept values that are classes themselves – specifically, it will accept the class object of C.

The documentation includes an example with the int class:

a = 3         # Has type 'int'
b = int       # Has type 'Type[int]'
c = type(a)   # Also has type 'Type[int]'