如何加载给定完整路径的Python模块?

请注意,文件可以位于文件系统中用户具有访问权限的任何位置。


另请参阅:如何导入以字符串形式命名的模块?


当前回答

我已经基于importlib模块编写了自己的全局和可移植导入函数,用于:

能够将两个模块作为子模块导入,并将模块的内容导入父模块(如果没有父模块,则导入全局模块)。能够导入文件名中包含句点字符的模块。能够导入具有任何扩展名的模块。能够为子模块使用独立名称,而不是默认的无扩展名的文件名。能够基于以前导入的模块而不是依赖于sys.path或搜索路径存储来定义导入顺序。

目录结构示例:

<root>
 |
 +- test.py
 |
 +- testlib.py
 |
 +- /std1
 |   |
 |   +- testlib.std1.py
 |
 +- /std2
 |   |
 |   +- testlib.std2.py
 |
 +- /std3
     |
     +- testlib.std3.py

包含依赖性和顺序:

test.py
  -> testlib.py
    -> testlib.std1.py
      -> testlib.std2.py
    -> testlib.std3.py

实施:

最新更改存储:https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/python/tacklelib/tacklelib.py

测试.py:

import os, sys, inspect, copy

SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("test::SOURCE_FILE: ", SOURCE_FILE)

# portable import to the global space
sys.path.append(TACKLELIB_ROOT) # TACKLELIB_ROOT - path to the library directory
import tacklelib as tkl

tkl.tkl_init(tkl)

# cleanup
del tkl # must be instead of `tkl = None`, otherwise the variable would be still persist
sys.path.pop()

tkl_import_module(SOURCE_DIR, 'testlib.py')

print(globals().keys())

testlib.base_test()
testlib.testlib_std1.std1_test()
testlib.testlib_std1.testlib_std2.std2_test()
#testlib.testlib.std3.std3_test()                             # does not reachable directly ...
getattr(globals()['testlib'], 'testlib.std3').std3_test()     # ... but reachable through the `globals` + `getattr`

tkl_import_module(SOURCE_DIR, 'testlib.py', '.')

print(globals().keys())

base_test()
testlib_std1.std1_test()
testlib_std1.testlib_std2.std2_test()
#testlib.std3.std3_test()                                     # does not reachable directly ...
globals()['testlib.std3'].std3_test()                         # ... but reachable through the `globals` + `getattr`

testlib.py:

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("1 testlib::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/std1', 'testlib.std1.py', 'testlib_std1')

# SOURCE_DIR is restored here
print("2 testlib::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/std3', 'testlib.std3.py')

print("3 testlib::SOURCE_FILE: ", SOURCE_FILE)

def base_test():
  print('base_test')

测试库.std1.py:

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std1::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/../std2', 'testlib.std2.py', 'testlib_std2')

def std1_test():
  print('std1_test')

测试库.std2.py:

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std2::SOURCE_FILE: ", SOURCE_FILE)

def std2_test():
  print('std2_test')

测试库.std3.py:

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std3::SOURCE_FILE: ", SOURCE_FILE)

def std3_test():
  print('std3_test')

输出(3.7.4):

test::SOURCE_FILE:  <root>/test01/test.py
import : <root>/test01/testlib.py as testlib -> []
1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib'])
base_test
std1_test
std2_test
std3_test
import : <root>/test01/testlib.py as . -> []
1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib', 'testlib_std1', 'testlib.std3', 'base_test'])
base_test
std1_test
std2_test
std3_test

在Python 3.7.4、3.2.5、2.7.16中测试

赞成的意见:

可以将两个模块作为子模块导入,也可以将模块的内容导入父模块(如果没有父模块,则导入全局模块)。可以导入文件名中包含句点的模块。可以从任何扩展模块导入任何扩展模块。可以为子模块使用独立名称,而不是默认的无扩展名的文件名(例如,testlib.std.py作为testlib,testlib.blabla.py作为testlib_blabla等)。不依赖于sys.path或搜索路径存储。不需要在调用tkl_import_module之间保存/还原全局变量,如SOURCE_FILE和SOURCE_DIR。[对于3.4.x及更高版本]可以在嵌套的tkl_import_module调用中混合模块名称空间(例如:named->local->named或local->named->local等)。[对于3.4.x及更高版本]可以自动将全局变量/函数/类从声明的位置导出到通过tkl_import_module(通过tkl_declare_global函数)导入的所有子模块。

欺骗:

不支持完全导入:忽略枚举和子类。忽略内置函数,因为必须以独占方式复制每种类型。忽略不可复制的类。避免复制内置模块,包括所有打包模块。[对于3.3.x及更低版本]要求在调用tkl_import_module的所有模块中声明tkl_import_module(代码重复)

更新1,2(仅适用于3.4.x及更高版本):

在Python 3.4及更高版本中,您可以通过在顶级模块中声明tkl_import_module来绕过在每个模块中声明tkl_import_mdule的要求,并且该函数将在一次调用中将自身注入到所有子模块中(这是一种自部署导入)。

更新3:

将函数tkl_source_module添加为模拟bash源代码,并在导入时支持执行保护(通过模块合并而不是导入实现)。

更新4:

添加了函数tkl_declare_global,以自动将模块全局变量导出到所有子模块,其中模块全局变量不可见,因为它不是子模块的一部分。

更新5:

所有函数都已移动到tacklelib库中,请参见上面的链接。

其他回答

创建Python模块test.py:

import sys
sys.path.append("<project-path>/lib/")
from tes1 import Client1
from tes2 import Client2
import tes3

创建Python模块test_check.py:

from test import Client1
from test import Client2
from test import test3

我们可以从模块导入导入的模块。

我发现这是一个简单的答案:

module = dict()

code = """
import json

def testhi() :
    return json.dumps({"key" : "value"}, indent = 4 )
"""

exec(code, module)
x = module['testhi']()
print(x)

要导入模块,需要将其目录临时或永久添加到环境变量中。

暂时

import sys
sys.path.append("/path/to/my/modules/")
import my_module

永久地

在Linux中将以下行添加到.bashrc(或替代)文件中以及终端中的exccute source~/.bashrc(或替代):

export PYTHONPATH="${PYTHONPATH}:/path/to/my/modules/"

信贷/来源:saarrrr,另一个Stack Exchange问题

可以使用__import__和chdir执行此操作:

def import_file(full_path_to_module):
    try:
        import os
        module_dir, module_file = os.path.split(full_path_to_module)
        module_name, module_ext = os.path.splitext(module_file)
        save_cwd = os.getcwd()
        os.chdir(module_dir)
        module_obj = __import__(module_name)
        module_obj.__file__ = full_path_to_module
        globals()[module_name] = module_obj
        os.chdir(save_cwd)
    except Exception as e:
        raise ImportError(e)
    return module_obj


import_file('/home/somebody/somemodule.py')

您也可以执行类似的操作,将配置文件所在的目录添加到Python加载路径中,然后进行正常导入,假设您事先知道文件的名称,在本例中为“config”。

凌乱,但它有效。

configfile = '~/config.py'

import os
import sys

sys.path.append(os.path.dirname(os.path.expanduser(configfile)))

import config