【问题标题】:Best way to create "virtual" modules in Python 3在 Python 3 中创建“虚拟”模块的最佳方式
【发布时间】:2020-07-29 20:26:47
【问题描述】:

问题描述

我有一个包含操作系统相关模块的包。这些模块每个都有相同的 API。

在客户端代码中,我可以执行以下操作:

import platform

if platform.system() == "Windows":
  from my_package import os_win32 as my_package_platform
elif platform.system() == "Linux":
  from my_package import os_linux as my_package_platform

# use my_package_platform in client code

但是我想做类似的事情

from my_package import my_package_platform

并让它自动导入正确的模块。

可能的解决方案 1:真实模块从特定模块导入所有内容

其实有一个模块my_package_platform.py

import platform
import importlib as imp
import importlib.util as impu
import sys

exports = ["symbol1", "symbol2"]

def update_globals(mod, var_list):
    for var in var_list:
        globals()[var] = mod.__dict__.get(var)


if platform.system() == "Windows":
    mod = imp.import_module(".os_win32", __package__)
elif platform.system() == "Linux":
    mod = imp.import_module(".os_linux", __package__)
else:
    raise NotImplementedError("not implemented for platform %s".format(platform.system()))

update_globals(mod, exports)

这似乎工作,但似乎有点hacky。还必须维护 exports 变量。

可能的解决方案 2:使用sys.meta_path 操作创建“虚拟模块”

在包的__init__.py中添加以下内容:

import platform
import importlib.abc
import importlib.util as impu
import sys


class _VirtualModuleFinder(importlib.abc.MetaPathFinder):

    def find_spec(self, fullname: str, path, target=None):
        if fullname == __package__ + ".my_package_platform":
            if platform.system() == "win32":
                return impu.find_spec(__package__ + ".os_win32")
            elif platform.system() == "Linux":
                return impu.find_spec(__package__ + ".os_linux")
            else:
                return None


sys.meta_path.append(_VirtualModuleFinder())

这很优雅,但它似乎有点矫枉过正。另一方面,附加的Finder 仅在通过其他方式找不到模块时才被调用。

需要注意的是,模块规范将包含原始模块名称。 (这可能是错误或功能 - 我目前不完全理解其中的含义)。

问题

  • 您认为每种方法都有哪些缺点?
  • 是否有更好的方法或“最佳实践”?

【问题讨论】:

    标签: python python-3.x architecture python-module


    【解决方案1】:

    我想我找到了一个更少被黑客入侵并且似乎更适合极端情况的解决方案:

    import platform
    import importlib as imp
    import importlib.util as impu
    import sys
    
    mpp_name = impu.resolve_name(".my_package_platform", __package__)
    
    if mpp_name not in sys.modules:
        if platform.system() == "win32":
            my_package_platform = imp.import_module(".os_win32", __package__)
        elif platform.system() == "Linux":
            my_package_platform = imp.import_module(".os_linux", __package__)
        else:
            raise ValueError("my_package_platform undefined for this platform")
    
        sys.modules[mpp_name] = my_package_platform
    

    甚至可以使用Mock.patch。仅缺少与 PyCharm 的 IDE 集成 - 不知道为什么 - 我猜他们有自己的模块搜索算法。

    【讨论】:

      猜你喜欢
      • 2018-11-11
      • 1970-01-01
      • 1970-01-01
      • 2018-11-09
      • 1970-01-01
      • 2019-12-09
      • 2012-09-18
      • 1970-01-01
      • 2011-11-27
      相关资源
      最近更新 更多