1. 使用 sys.path
import sys
sys.path.insert(0, 'c:/workspace')
# 假设该目录下有一个 hello_world 文件夹.
from hello_world import say_hello
say_hello() # -> 'hello world!'
2. 从 zip 文件加载
import sys
sys.path.append('c:/workspace/hello_world.zip')
from hello_world import say_hello
say_hello() # -> 'hello world!'
3. 使用 python 标准库 importlib 加载特定路径
import sys
from importlib import util
from os.path import basename
from os.path import exists
from types import ModuleType
def load_package_from_path(pkg_path: str) -> ModuleType:
"""
ref: https://stackoverflow.com/a/50395128
"""
init_path = f'{pkg_path}/__init__.py'
assert exists(init_path)
name = basename(pkg_path)
spec = util.spec_from_file_location(name, init_path)
module = util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
if __name__ == '__main__': # test
mod_a = load_package_from_path(
'c:/workspace/project_1/hello_world'
)
mod_b = load_package_from_path(
'c:/workspace/project_2/hello_world'
)
mod_a.say_hello() # -> 'hello world from alpha!'
mod_b.say_hello() # -> 'hello world from beta!'
python 在查找模块时, 会从 sys.path (类型为 list) 中寻找.
例如, 当我们 import hello_world, 那么就要保证 hello_world 的实际路径的 父目录 必须存在于 sys.path 中, 否则就会报 ModuleNotFoundError.
默认情况下, 我们从命令行启动 python, sys.path 的初始值是 (以 windows 为例):
[
'/python310.zip',
'/DLLs',
'',
'/lib/site-packages',
]
注意不同系统打印的结果存在一些差异. 使用 IDE 启动, 也会和命令行启动存在一些差异.
sys.path 查找模块的优先级 (从高到低):
该优先级决定了, 如果不同目录下有同名的子文件夹, 那么靠前的目录下的才会被成功导入.
缺点
直接在 sys.path 中添加该 zip 文件的路径即可, 可以是绝对路径, 也可以相对路径.
import sys
sys.path.append('c:/workspace/hello_world.zip')
from hello_world import say_hello
say_hello() # -> 'hello world!'
使用方法非常直观易懂. 而且不会带来类似 sys.path 的方法中那种导入空间被污染的隐患.
缺点
参考 SO 上的一个回答, 我个人觉得此方案非常优秀. 它有效解决了同名但不同目录的包的导入, 导入空间可能被污染的隐患, 以及导入效率等问题.
你可以将下面封装好的函数另存为一个 py 文件, 方便以后使用:
"""
filename: pyimport.py
usage:
from pyimport import load
mod_a = load('c:/workspace/project_1/hello_world')
mod_b = load('c:/workspace/project_2/hello_world')
mod_a.say_hello() # 'hello world from alpha!'
mod_b.say_hello() # 'hello world from beta!'
"""
import sys
from importlib import util
from os.path import basename
from os.path import exists
from types import ModuleType
def load_package_from_path(pkg_path: str) -> ModuleType:
"""
ref: https://stackoverflow.com/a/50395128
"""
init_path = f'{pkg_path}/__init__.py'
assert exists(init_path)
name = basename(pkg_path)
spec = util.spec_from_file_location(name, init_path)
module = util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
load = load_package_from_path # short alias