让我们考虑以下两种语法:
class Foo:
x: int
def __init__(self, an_int: int):
self.x = an_int
和
class Foo:
def __init__(self, an_int: int):
self.x = an_int
显然,以下代码在两种情况下都会引起mypy错误(这是预期的:]
obj = Foo(3)
obj.x.title() # this is a str operation
但是我真的很想执行合同:我想明确指出x是每个Foo
对象的实例变量。那么应该采用哪种语法,为什么呢?
这最终是个人喜好问题。要在另一个答案中使用该示例,请同时执行以下操作:
Optional
...正在做:
from typing import Union
class Foo:
x: Union[int, str]
def __init__(self, an_int: int):
self.x = an_int
def setx(self, a_str: str):
self.x = a_str
...类型检查器将以完全相同的方式对待。
做前者的主要优点是,在构造函数复杂到难以跟踪正在执行哪种类型推断的情况下,它使属性的类型更加明显。
此样式还与您声明和使用__init__
之类的内容一致:
class Foo:
x: Union[int, str]
def __init__(self, an_int: int) -> None:
self.x = an_int
这实际上不是一个优点或缺点,更多的是标准库在某些特定情况下采用了以前的样式。
[主要缺点是此样式有点冗长:您被迫重复两次重复变量名(如果包含class Foo:
def __init__(self, an_int: int) -> None:
self.x: Union[int, str] = an_int
参数,则被重复三次),并且常常被迫重复两次类型提示(一次在您变量注释,然后在dataclasses签名中输入)。
它还会在您的代码中带来一个可能的正确性问题:mypy永远不会实际检查以确保您已为属性分配任何内容!例如,尽管以下代码在运行时崩溃,但它们仍将很高兴地键入check:
from dataclasses import dataclass
@dataclass
class Foo:
x: int
y: Union[int, str]
z: str
# You get an `__init__` for free. Mypy will check to make sure the types match.
# So this type checks:
a = Foo(1, "b", "c")
# ...but this doesn't:
b = Foo("bad", 3.14, 0)
后一种样式避免了这些问题:如果您忘记分配属性,mypy将在以后尝试使用该属性时抱怨它不存在。
后一种样式的另一个主要优点是,您也可以不用花很多时间不添加显式类型提示,特别是如果您只是将参数直接分配给字段时。在这种情况下,类型检查器将推断出完全相同的类型。
因此,鉴于这些因素,我个人的偏好是:
__init__
的简单,类似记录的对象,请使用数据类(并通过代理使用以前的样式)。>__init__
,以减少冗长性和出现“忘记分配属性”错误的可能性,请使用后一种样式。class Foo:
x: int
def __init__(self, x: int) -> None:
# Whoops, I forgot to do 'self.x = x'
pass
f = Foo(1)
# Type checks, but crashes at runtime!
print(f.x)
,这有点难以阅读,请切换回以前的样式。 (或者更好,只是重构我的代码,这样我就可以使__init__
简单!)当然,您最终可能会权衡这些因素,并得出不同的权衡。
一个最终切线-操作时:
。此时,x没有值,因此实际上不作为变量存在。__init__
...您实际上没有在注释class variable
您正在创建的唯一东西是annotation
,它只是纯元数据,与变量本身不同。但是如果您这样做:
创建一个类变量和一个注释。令人困惑的是,尽管您可能正在创建class变量/属性(而不是instance变量/属性),但是mypy和其他类型检查器将继续进行操作,假设类型注释旨在专门注释< [实例属性。这种不一致在实践中通常并不重要,特别是如果您遵循避免对任何事物使用可变默认值的一般最佳实践,则尤其如此。但是,如果您想做一些特别的事,可能会引起一些意外。__init__
...然后您are
如果您希望mypy /其他类型检查器将您的注释理解为
类变量注释
,则需要使用__init__
类型:class Foo:
x: int