所以,我正在使用 Python 编写一个事件发射器类。
代码目前如下所示:
from typing import Callable, Generic, ParamSpec
P = ParamSpec('P')
class Event(Generic[P]):
def __init__(self):
...
def addHandler(self, action : Callable[P, None]):
...
def removeHandler(self, action : Callable[P, None]):
...
def fire(self, *args : P.args, **kwargs : P.kwargs):
...
如您所见,注释依赖于
ParamSpec
,仅在Python 3.10中添加到typing
。
虽然它在 Python 3.10(在我的机器上)中运行良好,但在 Python 3.9 及更早版本(在其他机器上)中却失败了,因为
ParamSpec
是一项新功能。
那么,在运行程序时如何避免导入
ParamSpec
或使用一些后备替代方案,同时不混淆在编辑器中的输入(pyright)?
typing_extensions
模块由python核心团队维护,支持python3.7
及更高版本,并且正是用于此目的。您只需检查 python 版本并选择正确的导入源即可:
import sys
if sys.version_info < (3, 10):
from typing_extensions import ParamSpec
else:
from typing import ParamSpec
这可以通过将
from typing import ...
包装成 if TYPE_CHECKING
来解决:
if TYPE_CHECKING:
from typing import Callable, Generic, ParamSpec
else:
# Fake ParamSpec
class ParamSpec:
def __init__(self, _):
self.args = None
self.kwargs = None
# Base class to be used instead Generic
class Empty:
pass
# Thing what returns Empty when called like Generic[P]
class _Generic:
def __getitem__(self, _):
return Empty
# Callable[anything] will return None
class _Callable:
def __getitem__(self, _):
return None
# Make instances
Callable = _Callable()
Generic = _Generic()
... code
if not TYPE_CHECKING:
# To allow usage of
# evt : Event[[int, str]]
# Store existing Event class
_Event = Event
class FakeEvent:
# Event(...) calls original constructor
def __call__(self, *args, **kwds):
return _Event(*args, **kwds)
# Event[...] returns original Event
def __getitem__(self, _):
return _Event
# Replace Event class
Event = FakeEvent()
这允许代码在旧版本的 Python 上运行,同时在编辑器中使用 3.10 版本的输入。