如何在给定完整路径的情况下动态导入模块?
How do I load a Python module given its full path?
Note that the file can be anywhere in the filesystem where the user has access rights.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
如果您的顶级模块不是文件,而是使用 __init__.py 打包为目录,那么接受的解决方案几乎可以工作,但不完全有效。 在 Python 3.5+ 中,需要以下代码(请注意添加的以 'sys.modules' 开头的行):
如果没有此行,当执行 exec_module 时,它会尝试将顶层 __init__.py 中的相对导入绑定到顶层模块名称——在本例中为“mymodule”。 但是“mymodule”尚未加载,因此您会收到错误“SystemError:父模块'mymodule'未加载,无法执行相对导入”。 所以加载之前需要先绑定名字。 这样做的原因是相对导入系统的基本不变量:“不变量的持有是,如果你有 sys.modules['spam'] 和 sys.modules['spam.foo'] (就像你在上述导入之后所做的那样) ),后者必须显示为前者的 foo 属性”如此处讨论。
If your top-level module is not a file but is packaged as a directory with __init__.py, then the accepted solution almost works, but not quite. In Python 3.5+ the following code is needed (note the added line that begins with 'sys.modules'):
Without this line, when exec_module is executed, it tries to bind relative imports in your top level __init__.py to the top level module name -- in this case "mymodule". But "mymodule" isn't loaded yet so you'll get the error "SystemError: Parent module 'mymodule' not loaded, cannot perform relative import". So you need to bind the name before you load it. The reason for this is the fundamental invariant of the relative import system: "The invariant holding is that if you have sys.modules['spam'] and sys.modules['spam.foo'] (as you would after the above import), the latter must appear as the foo attribute of the former" as discussed here.
您还可以执行类似的操作,并将配置文件所在的目录添加到 Python 加载路径中,然后执行正常导入,假设您提前知道文件的名称,在本例中为“config”。
凌乱,但它有效。
You can also do something like this and add the directory that the configuration file is sitting in to the Python load path, and then just do a normal import, assuming you know the name of the file in advance, in this case "config".
Messy, but it works.
听起来您不想专门导入配置文件(这会带来很多副作用和额外的复杂性)。 您只想运行它,并能够访问生成的名称空间。 标准库专门为此提供了一个 API,其形式为 runpy.run_path< /a>:
该接口在 Python 2.7 和 Python 3.2+ 中可用。
It sounds like you don't want to specifically import the configuration file (which has a whole lot of side effects and additional complications involved). You just want to run it, and be able to access the resulting namespace. The standard library provides an API specifically for that in the form of runpy.run_path:
That interface is available in Python 2.7 and Python 3.2+.
添加到 Sebastian Rittau'答案:
至少对于 CPython 来说,有 pydoc,虽然没有正式声明,但导入文件就是它的作用:
PS。为了完整起见,有在撰写本文时对当前实现的引用: pydoc.py ,我很高兴地说,按照 xkcd 1987 的脉络,它没有使用issue 21436 中提到的实现——至少不是逐字的。
To add to Sebastian Rittau's answer:
At least for CPython, there's pydoc, and, while not officially declared, importing files is what it does:
PS. For the sake of completeness, there's a reference to the current implementation at the moment of writing: pydoc.py, and I'm pleased to say that in the vein of xkcd 1987 it uses neither of the implementations mentioned in issue 21436 -- at least, not verbatim.
我提出了 @SebastianRittau 的精彩答案的稍微修改版本(我认为对于 Python > 3.4),这将允许您使用
spec_from_loader
而不是spec_from_file_location
:以显式方式编码路径的优点
SourceFileLoader
是 machinery 不会尝试从扩展名中找出文件的类型。 这意味着您可以使用此方法加载诸如.txt
文件之类的内容,但如果不指定加载程序,则无法使用spec_from_file_location
来执行此操作,因为.txt 不在
importlib.machinery.SOURCE_SUFFIXES
。
我已将基于此的实现以及 @SamGrondahl 的有用修改放入我的实用程序库中,哈吉斯。 该函数称为
haggis.load.load_module
。 它添加了一些巧妙的技巧,例如在加载模块名称空间时将变量注入到模块名称空间中的能力。I have come up with a slightly modified version of @SebastianRittau's wonderful answer (for Python > 3.4 I think), which will allow you to load a file with any extension as a module using
spec_from_loader
instead ofspec_from_file_location
:The advantage of encoding the path in an explicit
SourceFileLoader
is that the machinery will not try to figure out the type of the file from the extension. This means that you can load something like a.txt
file using this method, but you could not do it withspec_from_file_location
without specifying the loader because.txt
is not inimportlib.machinery.SOURCE_SUFFIXES
.I've placed an implementation based on this, and @SamGrondahl's useful modification into my utility library, haggis. The function is called
haggis.load.load_module
. It adds a couple of neat tricks, like the ability to inject variables into the module namespace as it is loaded.下面是一些适用于所有 Python 版本(从 2.7 到 3.5,甚至可能是其他版本)的代码。
我测试过。 它可能很难看,但到目前为止它是唯一一个适用于所有版本的。
Here is some code that works in all Python versions, from 2.7-3.5 and probably even others.
I tested it. It may be ugly, but so far it is the only one that works in all versions.
您可以使用
pkgutil
模块(特别是 < code>walk_packages 方法)获取当前目录中的包列表。 从这里开始,使用 importlib 机制导入您想要的模块就很简单了:You can use the
pkgutil
module (specifically thewalk_packages
method) to get a list of the packages in the current directory. From there it's trivial to use theimportlib
machinery to import the modules you want:我相信你可以使用
imp.find_module()
和imp.load_module()
加载指定的模块。 您需要将模块名称从路径中分离出来,即如果您想加载/home/mypath/mymodule.py
您需要执行以下操作: ...但这应该得到任务完成。
I believe you can use
imp.find_module()
andimp.load_module()
to load the specified module. You'll need to split the module name off of the path, i.e. if you wanted to load/home/mypath/mymodule.py
you'd need to do:...but that should get the job done.
您可以使用 __import__ 和 chdir 来完成此操作:
You can do this using
__import__
andchdir
:如果我们有脚本在同一个项目但是在不同的目录意味着,我们可以通过以下方法解决这个问题。
在这种情况下,
utils.py
位于src/main/util/
中If we have scripts in the same project but in different directory means, we can solve this problem by the following method.
In this situation
utils.py
is insrc/main/util/
您可以使用
imp 模块 中的方法。
You can use the
method from the imp module.
你的意思是加载还是导入?
您可以操作 sys.path 列表指定模块的路径,然后导入您的模块。 例如,给定一个模块:
您可以这样做:
Do you mean load or import?
You can manipulate the
sys.path
list specify the path to your module, and then import your module. For example, given a module at:You could do:
有一个 package 专门用于此目的:
它已在 Python 版本(Jython 和 PyPy 也是如此)上进行了测试,但是根据您项目的规模,这可能有点过分了。
There's a package that's dedicated to this specifically:
It's tested across Python versions (Jython and PyPy too), but it might be overkill depending on the size of your project.
创建Python模块test.py:
创建Python模块test_check.py:
我们可以从module中导入导入的模块。
Create Python module test.py:
Create Python module test_check.py:
We can import the imported module from module.
使用
importlib
而不是imp
包的简单解决方案(针对 Python 2.7 进行了测试,尽管它也应该适用于 Python 3):现在您可以直接使用导入的名称空间模块,如下所示:
此解决方案的优点是我们甚至不需要知道要导入的模块的实际名称,以便在我们的代码中使用它。 这很有用,例如在模块的路径是可配置参数的情况下。
A simple solution using
importlib
instead of theimp
package (tested for Python 2.7, although it should work for Python 3 too):Now you can directly use the namespace of the imported module, like this:
The advantage of this solution is that we don't even need to know the actual name of the module we would like to import, in order to use it in our code. This is useful, e.g. in case the path of the module is a configurable argument.
这应该有效
This should work
要从给定文件名导入模块,您可以临时扩展路径,并在finally块中恢复系统路径 参考:
To import a module from a given filename, you can temporarily extend the path, and restore the system path in the finally block reference:
我为您制作了一个使用
imp
的包。 我称之为import_file
,这就是它的使用方式:您可以在以下位置获取它:
http ://pypi.python.org/pypi/import_file
或位于
http:// code.google.com/p/import-file/
I made a package that uses
imp
for you. I call itimport_file
and this is how it's used:You can get it at:
http://pypi.python.org/pypi/import_file
or at
http://code.google.com/p/import-file/
Python 3.4的这个领域似乎理解起来极其曲折! 然而,通过使用 Chris Calloway 的代码作为开始,我设法让一些东西工作起来。 这是基本功能。
这似乎使用了 Python 3.4 中未弃用的模块。 我并不假装理解为什么,但它似乎是在程序内工作的。 我发现克里斯的解决方案可以在命令行上运行,但不能在程序内部运行。
This area of Python 3.4 seems to be extremely tortuous to understand! However with a bit of hacking using the code from Chris Calloway as a start I managed to get something working. Here's the basic function.
This appears to use non-deprecated modules from Python 3.4. I don't pretend to understand why, but it seems to work from within a program. I found Chris' solution worked on the command line but not from inside a program.
我并不是说它更好,但为了完整起见,我想建议
exec
函数,在 Python 2 和 Python 3 中均可用。exec
允许您在全局范围或以字典形式提供的内部范围内执行任意代码。例如,如果您有一个使用函数
foo()
存储在"/path/to/module
" 中的模块,则可以通过执行以下操作来运行它:更明确的是,您正在动态加载代码,并授予您一些额外的功能,例如提供自定义内置函数的能力。
如果通过属性而不是键进行访问对您来说很重要,您可以为全局变量设计一个自定义字典类,它提供此类访问权限,例如:
I'm not saying that it is better, but for the sake of completeness, I wanted to suggest the
exec
function, available in both Python 2 and Python 3.exec
allows you to execute arbitrary code in either the global scope, or in an internal scope, provided as a dictionary.For example, if you have a module stored in
"/path/to/module
" with the functionfoo()
, you could run it by doing the following:This makes it a bit more explicit that you're loading code dynamically, and grants you some additional power, such as the ability to provide custom builtins.
And if having access through attributes, instead of keys is important to you, you can design a custom dict class for the globals, that provides such access, e.g.:
在运行时导入包模块(Python配方)
http://code.activestate .com/recipes/223972/
Import package modules at runtime (Python recipe)
http://code.activestate.com/recipes/223972/
我基于
importlib
模块编写了自己的全局和可移植导入函数,用于:示例目录结构:
包含依赖项和顺序:
实现:
最新更改存储: https://github.com/andry81/tacklelib/tree/HEAD/python/tacklelib/tacklelib.py
test.py:
testlib.py :
testlib.std1.py:
testlib.std2.py:
testlib.std3.py:
输出 (< code>3.7.4):
在 Python
3.7.4
、3.2.5
、2.7.16
中测试优点:
testlib.std.py
astestlib
、testlib.std.py)。 blabla.py 为
testlib_blabla
等)。tkl_import_module
之间保存/恢复全局变量,例如SOURCE_FILE
和SOURCE_DIR
。3.4.x
及更高版本] 可以在嵌套的tkl_import_module
调用中混合模块命名空间(例如:named->local->named
或本地->命名->本地
等等)。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_module更新 3:
添加函数
tkl_source_module
与 bashsource
类似,并在导入时支持执行保护(通过模块合并而不是导入实现)。更新 4:
添加了函数
tkl_declare_global
以自动将模块全局变量导出到所有子模块,其中模块全局变量因不属于子模块而不可见。更新5:
所有函数已移至tracelib库中,请参阅上面的链接。
I have written my own global and portable import function, based on
importlib
module, for:sys.path
or on a what ever search path storage.The examples directory structure:
Inclusion dependency and order:
Implementation:
Latest changes store: https://github.com/andry81/tacklelib/tree/HEAD/python/tacklelib/tacklelib.py
test.py:
testlib.py:
testlib.std1.py:
testlib.std2.py:
testlib.std3.py:
Output (
3.7.4
):Tested in Python
3.7.4
,3.2.5
,2.7.16
Pros:
testlib.std.py
astestlib
,testlib.blabla.py
astestlib_blabla
and so on).sys.path
or on a what ever search path storage.SOURCE_FILE
andSOURCE_DIR
between calls totkl_import_module
.3.4.x
and higher] Can mix the module namespaces in nestedtkl_import_module
calls (ex:named->local->named
orlocal->named->local
and so on).3.4.x
and higher] Can auto export global variables/functions/classes from where being declared to all children modules imported through thetkl_import_module
(through thetkl_declare_global
function).Cons:
3.3.x
and lower] Require to declaretkl_import_module
in all modules which calls totkl_import_module
(code duplication)Update 1,2 (for
3.4.x
and higher only):In Python 3.4 and higher you can bypass the requirement to declare
tkl_import_module
in each module by declaretkl_import_module
in a top level module and the function would inject itself to all children modules in a single call (it's a kind of self deploy import).Update 3:
Added function
tkl_source_module
as analog to bashsource
with support execution guard upon import (implemented through the module merge instead of import).Update 4:
Added function
tkl_declare_global
to auto export a module global variable to all children modules where a module global variable is not visible because is not a part of a child module.Update 5:
All functions has moved into the tacklelib library, see the link above.
一种非常简单的方法:假设您想要使用相对路径 ../../MyLibs/pyfunc.py 导入文件,
但是如果您在没有防护的情况下进行导入,您最终可以获得很长的路径。
A quite simple way: suppose you want import file with relative path ../../MyLibs/pyfunc.py
But if you make it without a guard you can finally get a very long path.
这将允许在 3.4 中导入已编译的 (pyd) Python 模块:
This will allow imports of compiled (pyd) Python modules in 3.4:
在 Linux 中,在 Python 脚本所在的目录中添加符号链接是可行的。
即:
Python 解释器将创建
/absolute/path/to/script/module.pyc
,并且如果您更改/absolute/path/to/module/module 的内容,则会更新它。 py
.然后在文件 mypythonscript.py 中包含以下内容:
In Linux, adding a symbolic link in the directory your Python script is located works.
I.e.:
The Python interpreter will create
/absolute/path/to/script/module.pyc
and will update it if you change the contents of/absolute/path/to/module/module.py
.Then include the following in file mypythonscript.py:
这是我对这个问题的 2024 年解决方案 - 不需要
.py
文件的路径,模块文件夹父级的路径就足够了。最后一条语句是必要的,以确保完整的模块存在于 sys.modules 中(包括子模块)。
Here's my 2024 solution to this question - does not require path to a
.py
file, path to the parent of the module folder is sufficient.The last statement is necessary to ensure that the full module is present in
sys.modules
(including submodules).这是我仅使用 pathlib 的两个实用函数。 它从路径推断模块名称。
默认情况下,它会递归地加载文件夹中的所有 Python 文件,并用父文件夹名称替换 init.py。 但您也可以提供路径和/或全局变量来选择某些特定文件。
These are my two utility functions using only pathlib. It infers the module name from the path.
By default, it recursively loads all Python files from folders and replaces init.py by the parent folder name. But you can also give a Path and/or a glob to select some specific files.
让我们在
/path/to/file.py
定义的module.name
模块中添加MyClass
。 下面是我们如何从此模块导入MyClass
对于 Python 3.5+ 使用 (docs):
对于 Python 3.3 和 3.4 使用:(
尽管这在 Python 3.4 中已被弃用。)
对于 Python 2 使用:
已编译的 Python 文件有等效的便利函数和 DLL。
另请参阅http://bugs.python.org/issue21436。
Let's have
MyClass
inmodule.name
module defined at/path/to/file.py
. Below is how we importMyClass
from this moduleFor Python 3.5+ use (docs):
For Python 3.3 and 3.4 use:
(Although this has been deprecated in Python 3.4.)
For Python 2 use:
There are equivalent convenience functions for compiled Python files and DLLs.
See also http://bugs.python.org/issue21436.
向 sys.path 添加路径(相对于使用 imp)的优点是,它可以简化从单个包导入多个模块时的操作。 例如:
The advantage of adding a path to sys.path (over using imp) is that it simplifies things when importing more than one module from a single package. For example:
要导入模块,您需要将其目录临时或永久添加到环境变量中。
临时
永久
将以下行添加到 Linux 中的
.bashrc
(或替代)文件中并在终端中执行
source ~/.bashrc
(或替代方案):Credit/Source: saarrrrr, 另一个 StackExchange 问题
To import your module, you need to add its directory to the environment variable, either temporarily or permanently.
Temporarily
Permanently
Adding the following line to your
.bashrc
(or alternative) file in Linuxand excecute
source ~/.bashrc
(or alternative) in the terminal:Credit/Source: saarrrr, another Stack Exchange question