稀有情况回退的 None 值类型提示
为了避免输入错误,我经常会遇到同样的问题。
比如,我有一个函数 x
,它很少返回值 None
,大多数情况下返回的是 int
。
def x(i: int) -> Union[int, None]:
if i == 0:
return
return i
def test(i: int):
a = x(i)
# typing issue: *= not supported for types int | None and int
a *= 25
这个 x
在代码中使用得非常频繁,而且我已经检查过很多次,确认 x(i)
确实会返回 int
而不是 None
。
但是直接把它当作 int
使用时,会出现类型警告——比如说,你不能把可能是 None
的值进行乘法运算。
在这种情况下,最佳实践是什么呢?
我考虑过的想法有:
- 用
if a is None: return
来检查None
其实没有什么意义,因为这个情况已经是知道的了。 a *= 25 # type: ignore
会让a
的类型变成Unknown
。a = x(i) # type: int
可以消除警告,但会产生一个新的警告:“int | None 不能被赋值给 int”。a = cast(int, x(i))
,我还没有进行太多测试。
我通常会把 x
的返回类型改成只返回 int
,在 return # type: ignore
中加上 ignore
,并在文档字符串中提到它可能返回 None
,这样可以避免整个代码库被类型警告污染。这是最好的方法吗?
def x(i: int) -> int:
"""might also return `None`"""
if i == 0:
return # type: ignore
return i
1 个回答
5
这可能是一个例子,说明在某些情况下,抛出异常比用一个你根本不指望会被执行的返回语句要好。
def x(i: int) -> int:
if i == 0:
raise ValueError("didn't expect i==0")
return i
def test(i: int):
try:
a = x(i)
except ValueError:
pass
a *= 25
如果代码很有信心地验证了传入的参数x
是有效的,那么可以省略try
语句。
从静态分析的角度来看,这样说是对的:如果 x
返回了,那它肯定会返回一个int
类型的值。(至于它是否会返回,这又是另一个问题。)
理想情况下,你可以定义一个叫NonZeroInt
的类型,这样就可以把i == 0
当作类型错误,而不是值错误。
# Made-up special form RefinementType obeys
#
# isinstance(x, RefinementType[T, p]) == isinstance(x, T) and p(x)
NonZeroInt = RefinementType[int, lambda x: x != 0]
def x(i: NonZeroInt) -> int:
return i
x(0) # error: Argument 1 to "x" has incompatible type "int"; expected "NonZeroInt" [arg-type]
i: int = 0
x(i) # same error
j: NonZeroInt = 0 # error: Incompatible types in assignment (expression has type "int", variable has type "NonZeroInt") [assignment]
x(j) # OK
k: NonZeroInt = 3 # OK
x(k) # OK