如何解决具有默认值但没有类型注释的函数参数的推断类型是什么?初始化为“无”的变量如何? 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 docs和example code in the Python docs,foo
,bar
,baz
来看,当前分配了类型int
,False
和MyClass
。不过,这是否已在任何地方标准化?那bazz
又如何呢?那
def my_func(param1 = 2,param2 = False):
...
?类型检查器是否将传递给my_func
的参数分别设置为int
和bool
类型?
请注意,我对现状不怎么感兴趣,例如类型检查器(如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
。
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 举报,一经查实,本站将立刻删除。