我有以下 Python 代码,可以动态导入模块并动态设置类的属性:
class _ModuleRegistry(object):
_modules = {}
def defer_import(
self,
import_statement: str,
import_name: str,
):
self._modules[import_name] = import_statement
setattr(self, import_name, None)
def __getattribute__(self, __name: str):
if (
__name
and not __name.startswith("__")
and __name not in ("defer_import", "_modules")
):
import_statement = self._modules.get(__name)
if import_statement:
exec(import_statement, locals())
setattr(self, __name, locals().get(__name))
ret_val = locals().get(__name)
if ret_val:
return ret_val
else:
return None
else:
val = super().__getattribute__(__name)
return val
registry = _ModuleRegistry()
registry.defer_import("from pandas import read_csv", "read_csv")
print(registry.read_csv) # I want this to have function type hinting
我希望类型检查器能够推断
read_csv
函数的函数类型。
有办法做到这一点吗?
如果您的使用并不是真正动态的,并且您只是希望将导入推迟到调用函数时,那么您可以在键入存根 .pyi
文件或 TYPE_CHECKING
from typing import TYPE_CHECKING
if TYPE_CHECKING:
# Here I'm using `defaultdict` as an example, because mypy-play doesn't have typing
# information for `pandas`. But it should work the same.
from collections import defaultdict
from argparse import Namespace
registry = Namespace() # just an arbitrary object that supports attribute assignment
registry.defaultdict = defaultdict
else:
class _ModuleRegistry:
...
registry = _ModuleRegistry()
registry.defer_import("fromfrom collections import defaultdict", "defaultdict")
reveal_type(registry.defaultdict)
# Revealed type is "Overload(def [_KT, _VT] () -> collections.defaultdict[_KT`1, _VT`2], ...
(另请参阅mypy-play)但我仍然建议不要这样做,因为这是一种不寻常的模式,而且非常冗长。这确实感觉像是一个
XY 问题。如果目标是延迟导入,那么您可以使用内联导入
实现相同的目的,即将导入语句放在函数体中,直接在调用站点上方。还有具有惰性导入机制的 Python 解释器,例如Cinder,但这将是一个更大的变化。