让我们考虑以下两种语法变体:
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
对象的实例变量。那么应该首选哪种语法,为什么呢?
答案 0 :(得分:2)
这最终是个人喜好的问题。要在其他答案中使用该示例,请同时执行以下操作:
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之类的方式保持一致:
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)
这实际上不是一个利弊,只是更多观察到标准库在某些特定情况下采用了以前的样式。
主要缺点是此样式有点冗长:您被迫两次重复变量名(如果包含__init__
参数则被重复三次),并且经常被强制重复两次类型提示(一次出现在变量注释中,一次出现在__init__
签名中。
它还会在您的代码中带来一个可能的正确性问题:mypy永远不会实际检查以确保您已为属性分配了任何内容!例如,尽管以下代码在运行时崩溃,但仍将很高兴地键入check:
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)
后一种样式可避免这些问题:如果忘记分配属性,mypy将在以后尝试使用该属性时抱怨它不存在。
后一种样式的另一个主要优点是,您也可以不必花费很多时间不添加显式类型提示,尤其是在直接将参数分配给字段的情况下。在这种情况下,类型检查器会推断出完全相同的类型。
因此,考虑到这些因素,我个人比较喜欢:
__init__
的简单,类似记录的对象,请使用数据类(并通过代理使用以前的样式)。__init__
,请使用后一种样式,以减少冗长和出现“忘记分配属性”错误的几率。 __init__
,有点难以阅读,请切换回以前的样式。 (或更妙的是,只需重构代码即可使__init__
变得简单!)您最终可能会权衡这些因素,并得出一系列不同的权衡。
最后一条切线-完成时:
class Foo:
x: int
...您实际上并未在注释 class变量。此时,x没有值,因此实际上不作为变量存在。
您要创建的唯一内容是注释,它只是纯元数据,与变量本身不同。
但是,如果您这样做:
class Foo:
x: int = 3
...然后您 创建一个类变量和一个注释。有点令人困惑,尽管您可能正在创建 class 变量/属性(与 instance 变量/属性相对),但是mypy和其他类型检查器将继续假设类型注释为旨在专门注释 instance 属性。
这种不一致在实践中通常并不重要,尤其是如果您遵循避免对任何事物使用可变默认值的一般最佳实践。但是,如果您想做一些花哨的事情,这可能会引起一些意外。
如果您希望mypy /其他类型检查器将您的注释理解为类变量注释,则需要使用ClassVar
类型:
# Import this from 'typing_extensions' if you're using Python 3.7 or earlier
from typing import ClassVar
class Foo:
x: ClassVar[int] = 3
答案 1 :(得分:1)