动态类型代码(Dynamically)¶
在 动态与静态类型 中,我们讨论了函数体内没有任何显式类型注解的情况被称为“动态类型(dynamically typed)”,并且 mypy 不会对其进行检查。在本节中,我们将更详细地讨论这意味着什么,以及如何在更细粒度的基础上启用动态类型。
在您的代码过于复杂以至于 mypy 无法理解的情况下,您可以通过显式地将变量或参数的类型设置为 Any
来使其动态类型。Mypy 将允许您对类型为 Any
的值进行基本上任何操作,包括将类型为 Any
的值赋给任何类型的变量(或反之亦然)。
from typing import Any
num = 1 # 静态类型(推断为 int)
num = 'x' # 错误:赋值中的不兼容类型(表达式的类型为 "str",变量的类型为 "int")
dyn: Any = 1 # 动态类型(类型为 Any)
dyn = 'x' # OK
num = dyn # 没有错误,mypy 允许您将类型为 Any 的值赋给任何变量
num += 1 # 哦,mypy 仍然认为 num 是 int
您可以将 Any
视为局部禁用类型检查的一种方式。有关您可以使用的其他关闭类型检查器的方法,请参见 静默类型错误。
对 Any 值的操作¶
您可以对类型为 Any
的值执行任何操作,类型检查器不会发出警告:
def f(x: Any) -> int:
# 所有这些都是有效的!
x.foobar(1, y=2)
print(x[3] + 'f')
if x:
x.z = x(2)
open(x).read()
return x
从 Any
值派生的值通常也隐式地具有 Any
类型,因为 mypy 无法推断出更精确的结果类型。例如,如果您获取一个 Any
值的属性或调用 Any
值,结果就是 Any
:
def f(x: Any) -> None:
y = x.foo()
reveal_type(y) # 显示的类型是 "Any"
z = y.bar("mypy 会允许你对 y 做任何事")
reveal_type(z) # 显示的类型是 "Any"
Any
类型可能在程序中传播,除非您小心,否则会使类型检查的效果降低。
没有注释的函数参数也隐式地为 Any
:
def f(x) -> None:
reveal_type(x) # 显示的类型是 "Any"
x.can.do["anything", x]("wants", 2)
您可以使用 --disallow-untyped-defs
标志使 mypy 针对没有类型注解的函数参数发出警告。
缺少类型参数的泛型类型将隐式地将这些参数视为 Any
:
def f(x: list) -> None:
reveal_type(x) # 显示的类型是 "builtins.list[Any]"
reveal_type(x[0]) # 显示的类型是 "Any"
x[0].anything_goes() # OK
您可以使用 --disallow-any-generics
标志使 mypy 针对缺少类型参数的泛型类型发出警告。
最后, Any
类型泄漏到程序中的另一个主要来源是 mypy 不知道的第三方库。当使用 --ignore-missing-imports
标志时,尤其如此。有关此信息,请参见 缺失导入(Missing imports) 。
Any 与 object¶
类型 object
是另一种可以具有任意类型实例作为值的类型。
与 Any
不同,object
是一种普通的静态类型(类似于 Java 中的 Object
),并且仅接受对 所有 类型有效的操作。
以下都是有效的操作:
def f(o: object) -> None:
if o:
print(o)
print(isinstance(o, int))
o = 2
o = 'foo'
然而,以下操作会被标记为错误,因为并非所有对象都支持这些操作:
def f(o: object) -> None:
o.foo() # 错误!
o + 2 # 错误!
open(o) # 错误!
n: int = 1
n = o # 错误!
如果您不确定是使用 object
还是 Any
,请使用 object
—— 仅在出现类型检查器警告时才切换到使用 Any
。
您可以使用不同的 类型缩小 技巧将 object
缩小为更具体的类型(子类型),例如 int
。对于动态类型的值(类型为 Any
的值),不需要进行类型缩小。