类型提示是Python 3.5中讨论最多的功能之一。
的一个例子类型提示中提到的这篇文章和这一个,同时还提负责任地使用类型提示。有人可以解释更多有关它们的信息,何时使用以及何时不使用?
我建议阅读PEP 483和PEP 484和观看该演示由Guido的类型提示。
简而言之:类型提示实际上是单词的意思,你可以提示所使用的对象的类型。
由于Python 的动态特性,推断或检查所使用对象的类型特别困难。这个事实使开发人员很难理解他们尚未编写的代码中到底发生了什么,而且最重要的是,对于许多IDE所使用的类型检查工具(想到的PyCharm,PyDev),由于以下事实而受到限制:他们没有任何指示物是什么类型的指标。结果,他们求助于推断类型(如演示中所述),成功率约为50%。
摘录“类型提示”演示文稿中的两个重要幻灯片:
为什么要输入提示?
TypeErrors
。为什么要使用静态类型检查器?
作为此简短介绍的结束语:这是一项可选功能,据我了解,已引入此功能是为了获得静态类型化的某些好处。
通常,你无需担心它,并且绝对不需要使用它(尤其是在将Python用作辅助脚本语言的情况下)。在开发大型项目时,它应该很有帮助,因为它提供了非常需要的鲁棒性,控制和附加的调试功能。
用mypy输入提示:
为了使这个答案更完整,我认为进行一些演示是合适的。我将使用mypy,它启发了在PEP中呈现的Type Hints
的库。这主要是为任何遇到此问题并想从哪里开始的人编写的。
在此之前,我先重申以下内容:PEP 484
不执行任何操作;它只是为功能注释设置一个方向,并为如何 /
应该执行类型检查提出指南。你可以注释你的功能并根据需要提示任意多的内容。无论注释是否存在,你的脚本仍将运行,因为Python本身不使用它们。
无论如何,如PEP中所述,提示类型通常应采用三种形式:
# type: type
补充前两种形式的特殊注释。(请参阅:什么是Python 3.6中的变量注释?有关Python 3.6的# type: type注释更新)此外,你还需要将类型提示与中typing引入的新模块结合使用Py3.5。其中定义了许多(附加)ABC(抽象基类)以及用于静态检查的辅助函数和修饰器。包括大多数内容ABCs,collections.ab
c但Generic
以允许订阅的形式(通过定义__getitem__()
方法)。
对于那些对这些内容有更深入说明感兴趣的人,mypy documentation
它的编写非常好,并且有许多代码示例演示/说明了其检查器的功能。绝对值得一读。
功能注释和特殊注释:
首先,观察使用特殊注释时我们可以获得的某些行为很有趣。# type: type
如果不能直接推断出对象的类型,则可以在变量分配期间添加特殊注释以指示对象的类型。通常可以轻松推断出简单的任务,但是其他任务,例如列表(就其内容而言)则不能。
注意:如果要使用的任何派生形式Containers
并需要指定该容器的内容,则必须使用模块中的泛型类型typing
。这些支持索引。
# generic List, supports indexing.
from typing import List
# In this case, the type is easily inferred as type: int.
i = 0
# Even though the type can be inferred as of type list
# there is no way to know the contents of this list.
# By using type: List[str] we indicate we want to use a list of strings.
a = [] # type: List[str]
# Appending an int to our list
# is statically not correct.
a.append(i)
# Appending a string is fine.
a.append("i")
print(a) # [0, 'i']
如果我们将这些命令添加到文件中并使用解释器执行它们,则一切工作正常,print(a)
仅打印list的内容a
。这些# type
注释已被丢弃,被视为没有附加语义含义的普通注释。
mypy
另一方面,通过运行,我们得到以下响应:
(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
表示str对象列表不能包含int,从静态上讲是声音。可以通过遵循a和仅添加str对象的类型或通过更改内容的类型a以指示任何值都是可接受的来直观地解决此问题(直观地用List[Any]after Any导入from来执行typing)。
param_name : type
在函数签名中的每个参数之后,以形式添加函数注释,并在-> type结束函数冒号之前使用符号指定返回类型;所有注释都__annotations__
以方便的字典形式存储在该函数的属性中。使用一个简单的示例(不需要typing模块中的其他类型):
def annotated(x: int, y: str) -> bool:
return x < y
annotated.__annotations__
现在,该属性具有以下值:
{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
如果我们是一个完整的noobie
,或者我们熟悉Py2.7概念并且因此不知道TypeError
在比较时存在潜伏性annotated
,那么我们可以执行另一个静态检查,捕获错误并为我们节省一些麻烦:
(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
除其他外,使用无效参数调用该函数也将被捕获:
annotated(20, 20)
# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
这些基本可以扩展到任何用例,并且捕获的错误比基本的调用和操作还要扩展。你可以检查的类型非常灵活,而我仅给出了其潜能的一小部分。通过查看typing
模块,PEP
或mypy
文档,你可以更全面地了解所提供的功能。
存根文件:
存根文件可以在两种不同的非互斥情况下使用:
你需要键入检查你不想直接更改功能签名的模块
你想要编写模块并进行类型检查,但还希望将注释与内容分开。
存根文件(扩展名为.pyi)是你正在/将要使用的模块的带注释的接口。它们包含要键入的功能的签名,并丢弃了这些功能的主体。为了了解这一点,在名为的模块中提供了三个随机函数的集合randfunc.py:
def message(s):
print(s)
def alterContents(myIterable):
return [i for i in myIterable if i % 2 == 0]
def combine(messageFunc, itFunc):
messageFunc("Printing the Iterable")
a = alterContents(range(1, 20))
return set(a)
我们可以创建一个存根文件randfunc.pyi,如果需要的话,可以在其中放置一些限制。不利的一面是,在不了解存根的情况下查看源代码的人在试图理解应该传递到什么地方时并不会真正获得注释帮助。
无论如何,存根文件的结构都非常简单:将所有函数定义添加为空主体(pass填充),并根据需要提供注释。在这里,假设我们只想使用int容器的类型。
# Stub for randfucn.py
from typing import Iterable, List, Set, Callable
def message(s: str) -> None: pass
def alterContents(myIterable: Iterable[int])-> List[int]: pass
def combine(
messageFunc: Callable[[str], Any],
itFunc: Callable[[Iterable[int]], List[int]]
)-> Set[int]: pass
该combine函数提供了一个指示,说明为什么你可能要在其他文件中使用批注,它们有时会使代码混乱,并降低可读性(Python不能避免)。你当然可以使用类型别名,但有时会比其有用之处更加混乱(因此请明智地使用它们)。
这应该使你熟悉Python中类型提示的基本概念。即使使用了类型检查器, mypy你也应该逐渐开始看到它们的更多弹出窗口,其中一些内部是IDE(PyCharm),其他是标准python模块。当我找到它们(或建议)时,我将尝试在以下列表中添加其他检查器/相关程序包。
我知道的跳棋:
Mypy:如此处所述。
PyType:由Google使用,与我收集到的符号不同,可能值得一看。
相关软件包/项目:
类型:官方Python回购,其中包含标准库的各种存根文件。
typeshed实际上,该项目是你可以查看的最佳场所之一,以了解如何在自己的项目中使用类型提示。让我们以相应文件中该类的__init__
笨拙Counter为例.pyi:
class Counter(Dict[_T, int], Generic[_T]):
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, Mapping: Mapping[_T, int]) -> None: ...
@overload
def __init__(self, iterable: Iterable[_T]) -> None: ...
在哪里_T = TypeVar('_T')
用来定义泛型类。对于Counter该类,我们可以看到它可以在其初始值设定项中不接受任何参数,可以将Mapping任何类型的单个值获取为an,int 也可以采用Iterable任何类型的。
注意:我忘记提及的一件事是该typing模块是临时引入的。从PEP 411:
临时包可以在“升级”为“稳定”状态之前修改其API。一方面,这种状态为程序包提供了正式加入Python发行版的好处。另一方面,核心开发团队明确声明,对于包API的稳定性没有任何保证,在下一发行版中可能会更改。虽然这被认为是不太可能的结果,但是如果对它们的API或维护的担忧被证明是有根据的,那么甚至可以在不弃用期限的情况下从标准库中删除此类软件包。
所以在这里放些盐。我怀疑它将以重大方式被删除或更改,但是人们永远不会知道。
问题内容: class Node: def append_child(self, node: Node): if node != None: self.first_child = node self.child_nodes += [node] 我该怎么办?因为当我运行它时,它说。 我应该只删除and实例在函数中检查它吗?但是,我该如何访问的属性(我希望它是类的实例)? 我不知道如何在Python,
问题内容: 我想为模块(类“模块”)添加(Python3)类型提示。该包不提供一个,并且是返回模块对象为特定名称的构造器。 例: 至少在PyCharm中导致“在types.pyi中找不到引用ModuleType”。 请注意,使用Python输入模块类型不会回答我的问题,因为它不能解释ModuleType既是构造函数又是类型,如下所示。 问题答案: 并且是构造函数。 没关系 就像和are一样,仍然是
问题内容: 我想允许使用Python 3进行类型提示,以接受某个类的子类。例如: 现在,当输入以下代码时: 我得到一个PyCharm IDE提示“期望的类型A,取而代之的是Type [B]。” 如何更改此处的类型提示以接受A的任何子类型? 据此(https://www.python.org/dev/peps/pep-0484/#type-definition- syntax ,“该类型的参数也接受
我希望允许使用Python 3进行类型提示以接受某个类的子类。例如。: 现在当键入以下代码时: 我收到一个 PyCharm IDE 提示“预期的类型 A,而是获得类型 [B]。 如何在这里更改类型提示以接受? 根据PEP 484(“类型为特定参数类型的子类型的表达式也可用于该参数。”),我知道我的解决方案< code>(cls: A)应该可以工作?
问题内容: 在PEP 484中 ,包含模块的类型提示已添加到Python 3中。在Python 2中有什么方法可以做到这一点?我能想到的就是有一个装饰器来添加到检查类型的方法中,但这将在运行时失败,并且不会像提示所允许的那样被较早地捕获。 问题答案: 根据 Python 2.7的建议语法和 定义类型提示的PEP 484中的 跨代码 ,存在与Python 2.7兼容的另一种语法。但是,它不是强制性的
我试图在Python3.5中使用类型提示。1,代码如下: 我猜在解析类型A时,类型A还没有完全构造,这是范围界定的问题,但我不理解为什么这样的语法不正确。是否有其他方式来表达它,或者它只是一个非法的构造? 我想在基类中使用这个语法提示,它可以在树状层次结构中组成派生类。