微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

具有默认值但没有类型注释的函数参数的推断类型是什么?初始化为“无”的变量如何? PEP 484第"Scoping rules for type variables"节中的示例 PEP 484第"Instantiating generic classes and type erasure"节中的示例

如何解决具有默认值但没有类型注释的函数参数的推断类型是什么?初始化为“无”的变量如何? PEP 484第"Scoping rules for type variables"节中的示例 PEP 484第"Instantiating generic classes and type erasure"节中的示例

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

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

Mypy docsexample code in the Python docsfoobarbaz来看,当前分配了类型intFalseMyClass。不过,这是否已在任何地方标准化?那bazz又如何呢?那

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

?类型检查器是否将传递给my_func的参数分别设置为intbool类型?

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

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

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

解决方法

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

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

但是,截至2020年10月,PEP 484在"The Any type部分中规定:

假定没有注释的函数参数已用Any注释。

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

Jukka Lehtosalo写道:

Mypy跟随PEP 484没有注释的函数参数与具有Any注释的函数参数相同,默认值也不例外。这是一些基本原理:

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

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

然后Guido回答:

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

x = 0

推断int的类型为x。在实践中,这是非常普遍和有用的,仅偶尔需要帮助。因此,我认为我们可以对默认值使用相同的规则,并且解释事情的复杂性不会真正改变。如果有足够的人关心的话,我愿意对此进行PEP 484更改。

问题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]
,

关于此块的类型情况

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

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

因此,街区

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

对于baz而言,类本身是一种类型,因此您可以用类名对其进行注释。 对于bazz,我留下了?,因为您应该对它的类型有一个期望。可能是几种可能的类型并使用Union[int,float],或者您可能并不真正了解/关心并使用Any作为类型。

现在,关于功能

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

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

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

类型检查器不是强制执行器,python仍是一种动态语言。尽管可能会有工具/库利用注释来对类型进行一些运行时强制。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。