类型提示使用 importlib 动态导入模块
Typehint importing module dynamically using importlib
给点如下:
import importlib
module_path = "mod"
mod = importlib.import_module(module_path, package=None)
print(mod.Foo.Bar.x)
其中 mod.py
是:
class Foo:
class Bar:
x = 1
mypy file.py --strict
raises the following error:
file.py:7: error: Module has no attribute "Foo" [attr-defined]
我想知道应该如何进行类型提示,或者这是否是通常会被 # type: ignore[attr-defined]
忽略的东西(假设代码是必要的, 唯一的选择是类型提示或忽略类型提示)?
为什么我在这种情况下使用 importlib
importlib
的使用方式是有一些路径:
x.y.<changes>.z
其中<changes>
是动态的,其他都是固定的。我相信该模块将包含正在调用的属性,但由于 <changes>
,importlib
用于导入。
可以概括为:
I do not know precisely which module I will be importing, but I know it will have a class Foo
in it.
正如@MisterMiyagi 所暗示的那样,我认为这里的解决方案是使用结构化而不是名义上的子类型化。 Nominal 子类型是我们使用直接 class 继承来定义类型关系的地方。例如,collections.Counter
是 dict
的子类型,因为它直接继承自 dict
。然而,结构 子类型是我们根据 class 具有的某些属性或它显示的某些行为来定义类型的地方。 int
是 typing.SupportsFloat
的子类型,不是因为它直接继承自 SupportsFloat
(不是),而是因为 SupportsFloat
被定义为某个 接口 和 int
满足该接口。
当类型提示时,我们可以使用 typing.Protocol
定义结构类型。在这种情况下,您可以像这样满足 MyPy:
import importlib
from typing import cast, Protocol
class BarProto(Protocol):
x: int
class FooProto(Protocol):
Bar: type[BarProto]
class ModProto(Protocol):
Foo: type[FooProto]
module_path = "mod"
mod = cast(ModProto, importlib.import_module(module_path, package=None))
print(mod.Foo.Bar.x)
reveal_type(mod)
reveal_type(mod.Foo)
reveal_type(mod.Foo.Bar)
reveal_type(mod.Foo.Bar.x)
我们在这里定义了几个接口:
BarProto
:为了满足这个接口,一个类型必须有一个 x
类型的属性 int
.
FooProto
:为了满足此接口,类型必须具有属性 Bar
,即 class 的实例满足 BarProto
协议。
ModProto
:为了满足此接口,类型必须具有属性 Foo
,即 class 的实例满足 FooProto
协议。
然后,在导入模块时,我们使用 typing.cast
向类型检查器断言我们正在导入的模块满足 ModProto
协议。
运行 它 through MyPy,它告诉我们它已经推断出以下类型:
main.py:18: note: Revealed type is "__main__.ModProto"
main.py:19: note: Revealed type is "Type[__main__.FooProto]"
main.py:20: note: Revealed type is "Type[__main__.BarProto]"
main.py:21: note: Revealed type is "builtins.int"
给点如下:
import importlib
module_path = "mod"
mod = importlib.import_module(module_path, package=None)
print(mod.Foo.Bar.x)
其中 mod.py
是:
class Foo:
class Bar:
x = 1
mypy file.py --strict
raises the following error:
file.py:7: error: Module has no attribute "Foo" [attr-defined]
我想知道应该如何进行类型提示,或者这是否是通常会被 # type: ignore[attr-defined]
忽略的东西(假设代码是必要的, 唯一的选择是类型提示或忽略类型提示)?
为什么我在这种情况下使用 importlib
importlib
的使用方式是有一些路径:
x.y.<changes>.z
其中<changes>
是动态的,其他都是固定的。我相信该模块将包含正在调用的属性,但由于 <changes>
,importlib
用于导入。
可以概括为:
I do not know precisely which module I will be importing, but I know it will have a class
Foo
in it.
正如@MisterMiyagi collections.Counter
是 dict
的子类型,因为它直接继承自 dict
。然而,结构 子类型是我们根据 class 具有的某些属性或它显示的某些行为来定义类型的地方。 int
是 typing.SupportsFloat
的子类型,不是因为它直接继承自 SupportsFloat
(不是),而是因为 SupportsFloat
被定义为某个 接口 和 int
满足该接口。
当类型提示时,我们可以使用 typing.Protocol
定义结构类型。在这种情况下,您可以像这样满足 MyPy:
import importlib
from typing import cast, Protocol
class BarProto(Protocol):
x: int
class FooProto(Protocol):
Bar: type[BarProto]
class ModProto(Protocol):
Foo: type[FooProto]
module_path = "mod"
mod = cast(ModProto, importlib.import_module(module_path, package=None))
print(mod.Foo.Bar.x)
reveal_type(mod)
reveal_type(mod.Foo)
reveal_type(mod.Foo.Bar)
reveal_type(mod.Foo.Bar.x)
我们在这里定义了几个接口:
BarProto
:为了满足这个接口,一个类型必须有一个x
类型的属性int
.FooProto
:为了满足此接口,类型必须具有属性Bar
,即 class 的实例满足BarProto
协议。ModProto
:为了满足此接口,类型必须具有属性Foo
,即 class 的实例满足FooProto
协议。
然后,在导入模块时,我们使用 typing.cast
向类型检查器断言我们正在导入的模块满足 ModProto
协议。
运行 它 through MyPy,它告诉我们它已经推断出以下类型:
main.py:18: note: Revealed type is "__main__.ModProto"
main.py:19: note: Revealed type is "Type[__main__.FooProto]"
main.py:20: note: Revealed type is "Type[__main__.BarProto]"
main.py:21: note: Revealed type is "builtins.int"