具有默认值但没有类型注释的函数参数的推断类型是什么?初始化为 'None' 的变量怎么样?

What is the inferred type of a function argument with a default value but no type annotation? How about a variable initialized as 'None'?

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

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

Mypy docs and example code in the Python docsfoobarbaz 判断,当前 会被分配 intFalseMyClass。但是,这在任何地方都已标准化吗?那么 bazz 呢?那么

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

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

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

Type checkers are expected to attempt to infer as much information as necessary.

...但不清楚在上述情况下这到底意味着什么。 (毕竟,param1 = 2可能只是默认值,实际类型可能更复杂。)

关于该区块的类型情况

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

如果您希望检查它们,它们确实具有可以注释的类型。根据我的经验,在某些情况下 mypy 设法推断出某些类型,但通常是因为它们在其他地方“较早”被注释,例如 classes 或实例中属性的函数签名。

因此,块

foo: int = 2
bar: bool = False
baz: MyClass = MyClass()
bazz: Optional[?] = None

baz 的情况下,class 本身就是一个类型,因此您可以使用 class 的名称对其进行注释。 对于 bazz 我留下了 ? 因为你应该对它的类型有一个期望。也许它可以是几种可能的类型并使用 Union[int, float] 或者也许你不是真的 know/care 并使用 Any 作为类型。

现在,关于函数

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

如果该函数的签名被注释,类型检查器会告诉您它是否被正确调用。

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

类型检查器不是强制执行器,python 仍然是一种动态语言。尽管可能 tools/libraries 利用注释来执行某些类型的运行时强制执行。

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

2017 年,Guido van Rossum, the creator of the Python language, suggested changing PEP 484 指定函数参数将从默认值推断其类型。

但是,截至 2020 年 10 月,PEP 484 在 "The Any type 节中指出:

A function parameter without an annotation is assumed to be annotated with Any.

在下面来自 mypy issue #3090 的讨论点中(从默认值推断参数类型),Jukka 强调默认值不会改变推断的默认行为类型为 Any.

的未注释参数

Jukka Lehtosalo 写道:

Mypy follows PEP 484. A function parameter without an annotation is the same as having an Any annotation, and there is no exception for default values. Here is some rationale for this:

  • Often the default argument is not enough to infer a type. For example, None doesn't give enough context. With the current rules this doesn't pose a difficulty.
  • The default argument may not be enough to infer the correct type. For example, if the default is '' in Python 2, the correct type could well be Union[str, unicode]. If the default is 0, the correct type might well be float. If the programmer understands the rules for type inference they could override the default type with an annotation as needed, but this would add some extra complexity.
  • The default value could be a complex expression such as a function call, and thus the type may not [be] obvious from just the default value. Having an annotation makes code like this easier to read.
  • If a function has no annotation at all, then the argument type would have to be Any anyway. The current rule makes this consistent across functions with no annotations and functions with partial annotations.

It all boils down to the current rule being simple and obvious, and in practice it doesn't feel much of a burden to add a few extra : int annotations. There is no deep technical reason for the current rule, though it makes type inference easier for mypy.

然后Guido回复:

OTOH most of those also apply to regular assignments, and there the rule is that

x = 0

infers type int for x. In practice this is very common and useful and only occasionally needs help. So I think we might use the same rule for default values and the complexity of explaining things wouldn't really change. I'm willing to make this a PEP 484 change if enough people care.

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

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

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

则类型推断为Optional[int]。由于 bazz 的推断类型是 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 部分的示例 "Scoping rules for type variables"

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 部分的示例 "Instantiating generic classes and type erasure"

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]