Python 从绝对路径导入模块,而不修改sys.modules

Python 从绝对路径导入模块,而不修改sys.modules,python,import,module,Python,Import,Module,因此,我知道我可以在给定模块的文件路径的情况下加载python模块 def import_module_from_fpath(module_fpath): from os.path import basename, splitext, isdir, join, exists, dirname, split import platform if isdir(module_fpath): module_fpath = join(module_fpath, '_

因此,我知道我可以在给定模块的文件路径的情况下加载python模块

def import_module_from_fpath(module_fpath):
    from os.path import basename, splitext, isdir, join, exists, dirname, split
    import platform
    if isdir(module_fpath):
        module_fpath = join(module_fpath, '__init__.py')
    print('module_fpath = {!r}'.format(module_fpath))
    if not exists(module_fpath):
        raise ImportError('module_fpath={!r} does not exist'.format(
            module_fpath))
    python_version = platform.python_version()
    modname = splitext(basename(module_fpath))[0]
    if modname == '__init__':
        modname = split(dirname(module_fpath))[1]
    if python_version.startswith('2.7'):
        import imp
        module = imp.load_source(modname, module_fpath)
    elif python_version.startswith('3'):
        import importlib.machinery
        loader = importlib.machinery.SourceFileLoader(modname, module_fpath)
        module = loader.load_module()
        # module = loader.exec_module(modname)
    else:
        raise AssertionError('invalid python version={!r}'.format(
            python_version))
return module
但是,这似乎总是填充
sys.modules
中的条目

我有一个例子,在不同的路径中有两个不同的模块。无法从PYTHONPATH访问这两个模块。我希望能够以不同的功能本地加载这些模块,并且不让它们相互干扰。这个问题的关键在于两个模块都有相同的名称(不同的版本)。因此,我不希望
sys.modules
在两者之间缓存任何内容

我不确定在加载模块后从
sys.modules
中删除模块名是否安全。看起来可能是我做的一个快速测试:

    import shutil
    import ubelt as ub
    test_root = ub.ensure_app_cache_dir('test_fpath_import')
    # Clear the directory
    shutil.rmtree(test_root)
    test_root = ub.ensure_app_cache_dir('test_fpath_import')

    # -----
    # Define two temporary modules with the same name that are not in sys.path
    import sys, os, os.path
    from os.path import join

    # Even though they have the same name they have different values
    mod1_fpath = ub.ensuredir((test_root, 'path1', 'testmod'))
    ub.writeto(join(mod1_fpath, '__init__.py'), 'version = 1\nfrom . import sibling\na1 = 1')
    ub.writeto(join(mod1_fpath, 'sibling.py'), 'spam = \"ham\"\nb1 = 2')

    # Even though they have the same name they have different values
    mod2_fpath = ub.ensuredir((test_root, 'path2', 'testmod'))
    ub.writeto(join(mod2_fpath, '__init__.py'), 'version = 2\nfrom . import sibling\na2 = 3')
    ub.writeto(join(mod2_fpath, 'sibling.py'), 'spam = \"jam\"\nb2 = 4')

    # -----
    # Neither module should be importable through the normal mechanism
    try:
        import testmod
        assert False, 'should fail'
    except ImportError as ex:
        pass

    mod1 = import_module_from_fpath(mod1_fpath)
    print('mod1.version = {!r}'.format(mod1.version))
    print('mod1.version = {!r}'.format(mod1.version))
    print(mod1.version == 1, 'mod1 version is 1')
    print('mod1.a1 = {!r}'.format(mod1.a1))

    mod2 = import_module_from_fpath(mod2_fpath)
    print('mod2.version = {!r}'.format(mod2.version))
    print(mod2.version == 2, 'mod2 version is 2')
    print('mod2.a2 = {!r}'.format(mod1.a2))

    # BUT Notice how mod1 is mod2
    print(mod1 is mod2)

    # mod1 has attributes from mod1 and mod2
    print('mod1.a1 = {!r}'.format(mod1.a1))
    print('mod1.a2 = {!r}'.format(mod1.a2))
    print('mod2.a1 = {!r}'.format(mod2.a1))
    print('mod2.a2 = {!r}'.format(mod2.a2))

    # Both are version 2
    print('mod1.version = {!r}'.format(mod1.version))
    print('mod2.version = {!r}'.format(mod2.version))

    # However sibling always remains at version1 (ham)
    print('mod2.sibling.spam = {!r}'.format(mod2.sibling.spam))

    # now importing testmod works because it reads from sys.modules
    import testmod

    # reloading mod1 overwrites attrs again
    mod1 = ut.import_module_from_fpath(mod1_fpath)

    # Removing both from sys.modules
    del sys.modules['testmod']
    del sys.modules['testmod.sibling']
    mod2 = ut.import_module_from_fpath(mod2_fpath)

    print(not hasattr(mod2, 'a1'),
        'mod2 no longer has a1 and it reloads itself correctly')
上面的脚本说明了当尝试加载两个具有相同名称的不同模块时会发生什么:它们相互覆盖/补充。但是,如果我执行此导入,然后直接删除所有导入的模块,则似乎做了“正确”的事情

我的问题是:

  • 它真的在做“正确”的事情吗?或者我的脚本没有测试某些情况

  • 是否有一种方法可以在本地导入模块,而不将其添加到
    sys.modules
    (又称全局名称空间)?(只有
    sys.modules
    构成了模块的全局名称空间吗?)


  • (我只在Python3中测试了上述代码,不确定旧的
    imp
    模块是否与
    importlib
    模块执行相同的操作)

    您是否尝试过删除
    sys.modules
    中的模块条目(可能是在
    import\u module\u from\u fpath()
    的末尾)?是的,它似乎可以工作,但我不确定这是否会产生任何其他意外后果。因此,问题就来了。如果
    sys.modules
    中的内容是对该模块的唯一引用,它将被删除。这还意味着,如果其他代码试图导入相同的模块,它将再次导入,而不是按照正常情况使用缓存版本。由于这是一个相当不寻常的用法,我怀疑是否有人会给你一个全面的副作用列表。我的建议是试试看,自己看看。信不信由你,我正试图解决同样的问题(加载同名的不同模块),原因很不寻常。我找到了这篇文章:关于你的第一个问题。对于第二个问题,我认为这是不可能的-sys.modules被更新为
    \uuuu import\uuuu()
    ,这是实际导入任何内容的唯一方法。我正在根据PEP 302探索导入挂钩,但这似乎不太可能。