如何向此 Python 代码添加类型注释以动态为类分配属性?

问题描述 投票:0回答:1

我有以下 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
函数的函数类型。

有办法做到这一点吗?

python mypy typing
1个回答
0
投票
真正的“动态”代码本质上与“静态”类型检查器不兼容。如果您导入的模块无法在运行时确定,那么类型检查器就无法推断类型。

如果您的使用并不是真正动态的,并且您只是希望将导入推迟到调用函数时,那么您可以在键入存根 .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,但这将是一个更大的变化。

© www.soinside.com 2019 - 2024. All rights reserved.