我有一个大致如下的函数:
import datetime
from typing import Union
class Sentinel(object): pass
sentinel = Sentinel()
def func(
dt: datetime.datetime,
as_tz: Union[datetime.tzinfo, None, Sentinel] = sentinel,
) -> str:
if as_tz is not sentinel:
# Never reached if as_tz has wrong type (Sentinel)
dt = dt.astimezone(as_tz)
# ...
# do other meaningful stuff
# ...
return "foo"
这里使用
sentinel
值是因为None
已经是.astimezone()
的有效参数,所以其目的是正确识别用户根本不想调用.astimezone()
的情况。然而,
mypy
抱怨这种模式:错误:“datetime”的“astimezone”的参数1具有不兼容的类型
“Union[tzinfo,None,Sentinel];应为”可选[tzinfo]“
这似乎是因为
datetime
stub使用了:def astimezone(self, tz: Optional[_tzinfo] = ...) -> datetime: ...
但是,有没有办法让mypy知道,
sentinel
值永远不会因为.astimezone()
检查而传递给if
?或者这只是需要一个# type: ignore
而没有更干净的方法?另一个例子:
from typing import Optional
import requests
def func(session: Optional[requests.Session] = None):
new_session_made = session is None
if new_session_made:
session = requests.Session()
try:
session.request("GET", "https://a.b.c.d.com/foo")
# ...
finally:
if new_session_made:
session.close()
第二个和第一个一样,是“运行时安全的”(因为缺少更好的术语):调用
AttributeError
和None.request()
的None.close()
将无法访问或计算。然而,mypy仍然抱怨:mypytest.py:9: error: Item "None" of "Optional[Session]" has no attribute "request"
mypytest.py:13: error: Item "None" of "Optional[Session]" has no attribute "close"
我应该做些不同的事情吗?
最佳答案
您可以使用显式cast
:
from typing import cast
...
if as_tz is not sentinel:
# Never reached if as_tz has wrong type (Sentinel)
as_tz = cast(datetime.tzinfo, as_tz)
dt = dt.astimezone(as_tz)
和
new_session_made = session is None
session = cast(requests.Session, session)
您可以交替使用
assert
(尽管这是一个实际的运行时检查,而cast
更明确地说是一个no-op): assert isinstance(as_tz, datetime.tzinfo)
dt = dt.astimezone(as_tz)
和
new_session_made = session is None
assert session is not None
关于python - 用mypy处理条件逻辑+哨兵值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57959664/