首页 > 解决方案 > 具有默认值但没有类型注释的函数参数的推断类型是什么?初始化为“无”的变量怎么样?

问题描述

type()没有类型注释但具有初始值的变量和参数的类型(在类型注释的意义上,不是)是什么?例如,

foo = 2
bar = False
baz = MyClass()
bazz = None

Mypy 文档Python 文档中的示例代码来看,foo, bar,当前baz将被分配类型,和. 但是,这是否已在任何地方标准化?那么呢?还有呢intFalseMyClassbazz

def my_func(param1 = 2, param2 = False): 
    ...

? 类型检查器会强制传递给的参数分别为和my_func类型吗?intbool

请注意,我对现状不太感兴趣,例如 Mypy 等类型检查器的当前实现。相反,我想知道我的问题的答案是否已在任何地方标准化。不幸的是,PEP-484似乎对此没有任何说明,除了:

预计类型检查器会尝试推断出尽可能多的信息。

......但目前尚不清楚这在上述情况下的真正含义。(毕竟,param1 = 2可能只是默认值,它的实际类型在现实中可能更复杂。)

标签: pythontype-hintingmypy

解决方案


问题 1:具有默认值但没有类型注释的函数参数的推断类型是什么?

2017 年, Python 语言的创建者Guido van Rossum建议更改PEP 484以指定函数参数将从默认值推断其类型。

但是,截至 2020 年 10 月,PEP 484 在Any类型

假设没有注释的函数参数用 注释Any

mypy 问题 #3090从默认值推断参数类型)的以下讨论点中,Jukka 强调了这样一个事实,即默认值不会改变将未注释参数推断为具有类型的默认行为Any

尤卡·莱托萨洛写道:

Mypy 遵循PEP 484没有注解的函数参数和有注解是一样的Any,默认值也不例外。这是这样做的一些理由:

  • 通常默认参数不足以推断类型。例如,None没有提供足够的上下文。按照目前的规则,这不会造成困难。
  • 默认参数可能不足以推断出正确的类型。例如,如果默认值''在 Python 2 中,那么正确的类型很可能是Union[str, unicode]. 如果默认是0,那么正确的类型很可能是float。如果程序员理解类型推断的规则,他们可以根据需要使用注释覆盖默认类型,但这会增加一些额外的复杂性。
  • 默认值可能是一个复杂的表达式,例如函数调用,因此仅从默认值来看,类型可能并不明显。有一个注解使这样的代码更容易阅读。
  • 如果一个函数根本没有注释,那么参数类型Any无论如何都必须是。当前规则使没有注释的函数和具有部分注释的函数之间保持一致。

这一切都归结为当前规则简单明了,并且在实践中添加一些额外的 :int注释并没有太大的负担。当前规则没有深层的技术原因,尽管它使 mypy 的类型推断更容易。

然后圭多回应道:

OTOH 其中大多数也适用于常规作业,规则是

x = 0

推断. int_ x在实践中,这是非常常见和有用的,只是偶尔需要帮助。所以我认为我们可能会对默认值使用相同的规则,并且解释事物的复杂性不会真正改变。如果有足够多的人关心,我愿意将此更改为 PEP 484。

问题 2:初始化为的变量的推断类型是None什么?

当一个变量初始化为None时,直接推断该变量允许显式赋值给None。如果该变量稍后被分配一个值,例如:

bazz = None
bazz = 42  # type: Optional[int]

然后类型被推断为Optional[int]。由于推断的类型bazzis Optional[int],以后可以重新分配它None而不会出错。

bazz = None
bazz = 42
bazz = None  # Okay

但是,如果bazz没有被初始化为None,那么以下将是一个错误:

bazz = 42
bazz = None  # Error: expression has type "None", variable has type "int"

在没有类型注释的情况下初始化的推断变量类型

PEP 484没有明确讨论根据分配值的类型推断未注释变量的类型。但是,从 PEP 484 示例中的注释可以推断,未注释变量的类型确实是基于赋值推断的。

PEP 484 部分“类型变量的范围规则”中的示例

T = TypeVar('T')
S = TypeVar('S')
class Foo(Generic[T]):
    def method(self, x: T, y: S) -> S:
        ...

x = Foo()               # type: Foo[int]
y = x.method(0, "abc")  # inferred type of y is str

PEP 484 部分“实例化泛型类和类型擦除”中的示例

from typing import TypeVar, Generic

T = TypeVar('T')

class Node(Generic[T]):
    x = None  # type: T # Instance attribute (see below)
    def __init__(self, label: T = None) -> None:
        ...

x = Node('')  # Inferred type is Node[str]
y = Node(0)   # Inferred type is Node[int]
z = Node()    # Inferred type is Node[Any]

推荐阅读