MyPy 检查 typing.Protocol 与 Python 3.7 支持
MyPy checking typing.Protocol with Python 3.7 Support
我有一个项目需要支持 Python 3.7,但我想使用 typing.Protocol
,它是在 3.8 中添加的。为了支持 3.7,我有一小段仅使用 object
:
的后备代码
import typing
_Protocol = getattr(typing, 'Protocol', object)
class Foo(_Protocol):
def bar(self) -> int:
pass
这一切都符合我的预期。问题是,当 运行 MyPy 时,出现以下错误:
test.py:5: error: Variable "test._Protocol" is not valid as a type
test.py:5: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
test.py:5: error: Invalid base class "_Protocol"
该错误消息中链接的 "Variables vs type aliases" 部分表明我应该用 : typing.Type[object]
注释 _Protocol
(这不起作用)或使用 typing.TypeAlias
(这不是'直到 Python 3.9)。
如何向 MyPy 指示 _Protocol
作为类型有效?
我尝试的另一个解决方法是 "Python version and system platform checks":
if sys.version_info >= (3, 8):
_Protocol = typing.Protocol
else:
_Protocol = object
然而,这以同样的错误结束。
使用typing_extensions
和typing.TYPE_CHECKING
仅在type-checking代码时导入typing_extensions
。
import typing
if typing.TYPE_CHECKING:
from typing_extensions import Protocol
else:
Protocol = object
class Foo(Protocol):
def bar(self) -> int:
...
typing_extensions checks Python 版本并使用 typing.Protocol
版本 >=3.8
:
# 3.8+
if hasattr(typing, 'Protocol'):
Protocol = typing.Protocol
# 3.7
else:
...
MyPy 似乎可以正确理解 Protocol
作为导入语句的一部分:
if sys.version_info >= (3, 8):
from typing import Protocol as _Protocol
else:
_Protocol = object
class Foo(_Protocol):
def bar(self) -> int: ...
MyPy 仍会将 Foo
的 usage 标记为 Python 3.7 中的类型规范,因此您需要类似的解决方法来使用它:
if sys.version_info >= (3, 8):
_Foo = Foo
else:
_Foo = typing.Any
def bar(x: _Foo):
pass
值得注意的是,MyPy 不 理解更聪明的东西,比如 _Foo = Foo if sys.version_info >= (3, 8) else typing.Any
-- 你必须使用简单的格式。
我有一个项目需要支持 Python 3.7,但我想使用 typing.Protocol
,它是在 3.8 中添加的。为了支持 3.7,我有一小段仅使用 object
:
import typing
_Protocol = getattr(typing, 'Protocol', object)
class Foo(_Protocol):
def bar(self) -> int:
pass
这一切都符合我的预期。问题是,当 运行 MyPy 时,出现以下错误:
test.py:5: error: Variable "test._Protocol" is not valid as a type
test.py:5: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
test.py:5: error: Invalid base class "_Protocol"
该错误消息中链接的 "Variables vs type aliases" 部分表明我应该用 : typing.Type[object]
注释 _Protocol
(这不起作用)或使用 typing.TypeAlias
(这不是'直到 Python 3.9)。
如何向 MyPy 指示 _Protocol
作为类型有效?
我尝试的另一个解决方法是 "Python version and system platform checks":
if sys.version_info >= (3, 8):
_Protocol = typing.Protocol
else:
_Protocol = object
然而,这以同样的错误结束。
使用typing_extensions
和typing.TYPE_CHECKING
仅在type-checking代码时导入typing_extensions
。
import typing
if typing.TYPE_CHECKING:
from typing_extensions import Protocol
else:
Protocol = object
class Foo(Protocol):
def bar(self) -> int:
...
typing_extensions checks Python 版本并使用 typing.Protocol
版本 >=3.8
:
# 3.8+
if hasattr(typing, 'Protocol'):
Protocol = typing.Protocol
# 3.7
else:
...
MyPy 似乎可以正确理解 Protocol
作为导入语句的一部分:
if sys.version_info >= (3, 8):
from typing import Protocol as _Protocol
else:
_Protocol = object
class Foo(_Protocol):
def bar(self) -> int: ...
MyPy 仍会将 Foo
的 usage 标记为 Python 3.7 中的类型规范,因此您需要类似的解决方法来使用它:
if sys.version_info >= (3, 8):
_Foo = Foo
else:
_Foo = typing.Any
def bar(x: _Foo):
pass
值得注意的是,MyPy 不 理解更聪明的东西,比如 _Foo = Foo if sys.version_info >= (3, 8) else typing.Any
-- 你必须使用简单的格式。