如何导入给定完整路径的模块?
-
09-06-2019 - |
题
如何在给定完整路径的情况下加载 Python 模块?请注意,该文件可以位于文件系统中的任何位置,因为它是一个配置选项。
解决方案
对于 Python 3.5+ 使用:
import importlib.util
spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py")
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
foo.MyClass()
对于 Python 3.3 和 3.4 使用:
from importlib.machinery import SourceFileLoader
foo = SourceFileLoader("module.name", "/path/to/file.py").load_module()
foo.MyClass()
(尽管这在 Python 3.4 中已被弃用。)
对于 Python 2 使用:
import imp
foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
对于已编译的 Python 文件和 DLL,也有等效的便利函数。
其他提示
向 sys.path 添加路径(相对于使用 imp)的优点是,它可以简化从单个包导入多个模块时的操作。例如:
import sys
# the mock-0.3.1 dir contains testcase.py, testutils.py & mock.py
sys.path.append('/foo/bar/mock-0.3.1')
from testcase import TestCase
from testutils import RunTests
from mock import Mock, sentinel, patch
您还可以执行类似的操作,并将配置文件所在的目录添加到 Python 加载路径中,然后执行正常导入,假设您提前知道文件的名称,在本例中为“config”。
凌乱,但它有效。
configfile = '~/config.py'
import os
import sys
sys.path.append(os.path.dirname(os.path.expanduser(configfile)))
import config
听起来您不想专门导入配置文件(它有很多副作用和额外的复杂性),您只想运行它,并能够访问生成的名称空间。标准库专门为此提供了一个API,其形式为 runpy.run_path:
from runpy import run_path
settings = run_path("/path/to/file.py")
该接口在 Python 2.7 和 Python 3.2+ 中可用
如果您的顶级模块不是文件,而是使用 __init__.py 打包为目录,那么接受的解决方案几乎可以工作,但不完全有效。在 Python 3.5+ 中,需要以下代码(请注意添加的以“sys.modules”开头的行):
MODULE_PATH = "/path/to/your/module/__init__.py"
MODULE_NAME = "mymodule"
import importlib
import sys
spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
如果没有这一行,当执行 exec_module 时,它会尝试将顶级 __init__.py 中的相对导入绑定到顶级模块名称 - 在本例中为“mymodule”。但“mymodule”尚未加载,因此您会收到错误“SystemError:父模块“mymodule”未加载,无法执行相对导入”。所以加载之前需要先绑定名字。其原因是相对导入系统的基本不变量:“不变的是,如果您有 sys.modules['spam'] 和 sys.modules['spam.foo'] (正如您在上述导入后所做的那样),后者必须显示为前者的 foo 属性” 正如这里所讨论的.
我想出了一个稍微修改过的版本 @SebastianRittau 的精彩回答 (我认为对于Python > 3.4),这将允许您使用以下命令加载具有任何扩展名的文件作为模块 spec_from_loader
代替 spec_from_file_location
:
from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader
spec = spec_from_loader("module.name", SourceFileLoader("module.name", "/path/to/file.py"))
mod = module_from_spec(spec)
spec.loader.exec_module(mod)
以显式方式编码路径的优点 SourceFileLoader
那是 机械 不会尝试从扩展名中找出文件的类型。这意味着您可以加载类似的东西 .txt
文件使用此方法,但你不能这样做 spec_from_file_location
没有指定加载器,因为 .txt
不在 importlib.machinery.SOURCE_SUFFIXES
.
下面是一些适用于所有 Python 版本(从 2.7 到 3.5,甚至可能是其他版本)的代码。
config_file = "/tmp/config.py"
with open(config_file) as f:
code = compile(f.read(), config_file, 'exec')
exec(code, globals(), locals())
我测试过。它可能很难看,但迄今为止是唯一一个适用于所有版本的。
要导入模块,您需要将其目录临时或永久添加到环境变量中。
暂时地
import sys
sys.path.append("/path/to/my/modules/")
import my_module
永久
将以下行添加到您的 .bashrc
文件(在linux中)并执行 source ~/.bashrc
在终端中:
export PYTHONPATH="${PYTHONPATH}:/path/to/my/modules/"
信用/来源: 萨尔, 另一个 stackexchange 问题
你的意思是加载还是导入?
您可以操作 sys.path 列表指定模块的路径,然后导入模块。例如,给定一个模块:
/foo/bar.py
你可以这样做:
import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar
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:
raise ImportError
import_file('/home/somebody/somemodule.py')
我相信你可以使用 imp.find_module()
和 imp.load_module()
加载指定的模块。您需要将模块名称从路径中分离出来,即如果你想加载 /home/mypath/mymodule.py
你需要这样做:
imp.find_module('mymodule', '/home/mypath/')
...但这应该可以完成工作。
这应该有效
path = os.path.join('./path/to/folder/with/py/files', '*.py')
for infile in glob.glob(path):
basename = os.path.basename(infile)
basename_without_extension = basename[:-3]
# http://docs.python.org/library/imp.html?highlight=imp#module-imp
imp.load_source(basename_without_extension, infile)
您可以使用 pkgutil
模块(特别是 walk_packages
方法)获取当前目录中的包列表。从那里开始使用就很简单了 importlib
导入所需模块的机器:
import pkgutil
import importlib
packages = pkgutil.walk_packages(path='.')
for importer, name, is_package in packages:
mod = importlib.import_module(name)
# do whatever you want with module now, it's been imported!
Python 3.4的这个领域似乎理解起来极其曲折!然而,通过使用 Chris Calloway 的代码作为开始,我设法让一些东西工作起来。这是基本功能。
def import_module_from_file(full_path_to_module):
"""
Import a module given the full path/filename of the .py file
Python 3.4
"""
module = None
try:
# Get module name and path from full path
module_dir, module_file = os.path.split(full_path_to_module)
module_name, module_ext = os.path.splitext(module_file)
# Get module "spec" from filename
spec = importlib.util.spec_from_file_location(module_name,full_path_to_module)
module = spec.loader.load_module()
except Exception as ec:
# Simple error printing
# Insert "sophisticated" stuff here
print(ec)
finally:
return module
这似乎使用了 Python 3.4 中未弃用的模块。我并不假装理解为什么,但它似乎是在程序内工作的。我发现克里斯的解决方案可以在命令行上运行,但不能在程序内部运行。
我并不是说它更好,但为了完整起见,我想建议 exec
函数,在 python 2 和 3 中都可用。exec
允许您在全局范围或内部范围(以字典形式提供)中执行任意代码。
例如,如果您有一个模块存储在 "/path/to/module
“ 与功能 foo()
, ,您可以通过执行以下操作来运行它:
module = dict()
with open("/path/to/module") as f:
exec(f.read(), module)
module['foo']()
这使得动态加载代码更加明确,并授予您一些额外的功能,例如提供自定义内置函数的能力。
如果通过属性而不是键进行访问对您来说很重要,您可以为全局变量设计一个自定义字典类,它提供此类访问权限,例如:
class MyModuleClass(dict):
def __getattr__(self, name):
return self.__getitem__(name)
要从给定文件名导入模块,可以暂时扩展路径,并在finally块中恢复系统路径 参考:
filename = "directory/module.py"
directory, module_name = os.path.split(filename)
module_name = os.path.splitext(module_name)[0]
path = list(sys.path)
sys.path.insert(0, directory)
try:
module = __import__(module_name)
finally:
sys.path[:] = path # restore
创建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 中导入导入的模块。
我做了一个包,使用 imp
为你。我称之为 import_file
这就是它的使用方式:
>>>from import_file import import_file
>>>mylib = import_file('c:\\mylib.py')
>>>another = import_file('relative_subdir/another.py')
您可以在以下位置获取:
http://pypi.python.org/pypi/import_file
或
在运行时导入包模块(Python 配方)
http://code.activestate.com/recipes/223972/
###################
## #
## classloader.py #
## #
###################
import sys, types
def _get_mod(modulePath):
try:
aMod = sys.modules[modulePath]
if not isinstance(aMod, types.ModuleType):
raise KeyError
except KeyError:
# The last [''] is very important!
aMod = __import__(modulePath, globals(), locals(), [''])
sys.modules[modulePath] = aMod
return aMod
def _get_func(fullFuncName):
"""Retrieve a function object from a full dotted-package name."""
# Parse out the path, module, and function
lastDot = fullFuncName.rfind(u".")
funcName = fullFuncName[lastDot + 1:]
modPath = fullFuncName[:lastDot]
aMod = _get_mod(modPath)
aFunc = getattr(aMod, funcName)
# Assert that the function is a *callable* attribute.
assert callable(aFunc), u"%s is not callable." % fullFuncName
# Return a reference to the function itself,
# not the results of the function.
return aFunc
def _get_class(fullClassName, parentClass=None):
"""Load a module and retrieve a class (NOT an instance).
If the parentClass is supplied, className must be of parentClass
or a subclass of parentClass (or None is returned).
"""
aClass = _get_func(fullClassName)
# Assert that the class is a subclass of parentClass.
if parentClass is not None:
if not issubclass(aClass, parentClass):
raise TypeError(u"%s is not a subclass of %s" %
(fullClassName, parentClass))
# Return a reference to the class itself, not an instantiated object.
return aClass
######################
## Usage ##
######################
class StorageManager: pass
class StorageManagerMySQL(StorageManager): pass
def storage_object(aFullClassName, allOptions={}):
aStoreClass = _get_class(aFullClassName, StorageManager)
return aStoreClass(allOptions)
在 Linux 中,在 python 脚本所在的目录中添加符号链接是可行的。
IE:
ln -s /absolute/path/to/module/module.py /absolute/path/to/script/module.py
python 将创建 /absolute/path/to/script/module.pyc
如果您更改内容,将会更新它 /absolute/path/to/module/module.py
然后在 mypythonscript.py 中包含以下内容
from module import *
很简单的方法:假设您想要使用相对路径 ../../MyLibs/pyfunc.py 导入文件
libPath = '../../MyLibs'
import sys
if not libPath in sys.path: sys.path.append(libPath)
import pyfunc as pf
但如果你在没有守卫的情况下成功,你最终会得到一条很长的路
一个简单的解决方案使用 importlib
而不是 imp
包(针对 Python 2.7 进行了测试,尽管它也应该适用于 Python 3):
import importlib
dirname, basename = os.path.split(pyfilepath) # pyfilepath: '/my/path/mymodule.py'
sys.path.append(dirname) # only directories should be added to PYTHONPATH
module_name = os.path.splitext(basename)[0] # '/my/path/mymodule.py' --> 'mymodule'
module = importlib.import_module(module_name) # name space of defined module (otherwise we would literally look for "module_name")
现在您可以直接使用导入模块的命名空间,如下所示:
a = module.myvar
b = module.myfunc(a)
该解决方案的优点是 我们甚至不需要知道要导入的模块的实际名称, ,以便在我们的代码中使用它。这很有用,例如如果模块的路径是可配置参数。
将其添加到答案列表中,因为我找不到任何有效的内容。这将允许在 3.4 中导入已编译的 (pyd) python 模块:
import sys
import importlib.machinery
def load_module(name, filename):
# If the Loader finds the module name in this list it will use
# module_name.__file__ instead so we need to delete it here
if name in sys.modules:
del sys.modules[name]
loader = importlib.machinery.ExtensionFileLoader(name, filename)
module = loader.load_module()
locals()[name] = module
globals()[name] = module
load_module('something', r'C:\Path\To\something.pyd')
something.do_something()
这个答案是对 Sebastian Rittau 回应评论的答案的补充:“但是,如果您没有模块名称怎么办?”这是获取可能给定文件名的可能的python模块名称的快速而肮脏的方法 - 它只是向上爬上树,直到找到一个目录而没有一个 __init__.py
文件,然后将其恢复为文件名。对于 Python 3.4+(使用 pathlib),这是有意义的,因为 Py2 人们可以使用“imp”或其他方式进行相对导入:
import pathlib
def likely_python_module(filename):
'''
Given a filename or Path, return the "likely" python module name. That is, iterate
the parent directories until it doesn't contain an __init__.py file.
:rtype: str
'''
p = pathlib.Path(filename).resolve()
paths = []
if p.name != '__init__.py':
paths.append(p.stem)
while True:
p = p.parent
if not p:
break
if not p.is_dir():
break
inits = [f for f in p.iterdir() if f.name == '__init__.py']
if not inits:
break
paths.append(p.stem)
return '.'.join(reversed(paths))
当然还有改进的可能性,并且可选的 __init__.py
文件可能需要其他更改,但如果您有 __init__.py
一般来说,这可以解决问题。
我认为最好的方法是来自官方文档(29.1。imp — 访问导入内部):
import imp
import sys
def __import__(name, globals=None, locals=None, fromlist=None):
# Fast path: see if the module has already been imported.
try:
return sys.modules[name]
except KeyError:
pass
# If any of the following calls raises an exception,
# there's a problem we can't handle -- let the caller handle it.
fp, pathname, description = imp.find_module(name)
try:
return imp.load_module(name, fp, pathname, description)
finally:
# Since we may exit via an exception, close fp explicitly.
if fp:
fp.close()