给定完整路径如何导入模块?

der*_*red 1022 python configuration python-module python-import

如何在完整路径下加载Python模块?请注意,该文件可以位于文件系统中的任何位置,因为它是一个配置选项.

Seb*_*tau 1166

对于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()
Run Code Online (Sandbox Code Playgroud)

对于Python 3.3和3.4使用:

from importlib.machinery import SourceFileLoader

foo = SourceFileLoader("module.name", "/path/to/file.py").load_module()
foo.MyClass()
Run Code Online (Sandbox Code Playgroud)

(虽然这在Python 3.4中已被弃用.)

Python 2使用:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Run Code Online (Sandbox Code Playgroud)

编译的Python文件和DLL有相同的便利功能.

也可以看看.http://bugs.python.org/issue21436.

  • @SridharRatnakumar"imp.load_source"的第一个参数的值只设置返回模块的`.__ name__`.它不会影响加载. (53认同)
  • 如果我知道名称空间 - 'module.name' - 我已经使用了`__import__`. (41认同)
  • @DanD. - `imp.load_source()`的第一个参数确定在`sys.modules`字典中创建的新条目的键,因此第一个参数确实影响加载. (16认同)
  • 从版本3.4开始,不推荐使用`imp`模块:`imp`包正在等待弃用,转而使用`importlib`. (9认同)
  • @AXO以及更多关于人们想知道为什么像这个简单和基本的东西*变得如此复杂.它不是很多其他语言. (8认同)
  • @ Mahesha999因为importlib.import_module()不允许你按文件名导入模块,这就是最初的问题. (4认同)
  • 对于Python 3.5+,如果`/ path/to/file.py`隐式导入兄弟(例如`import bar`导入`/ path/to/bar.py`),解决方案会产生`ModuleNotFoundError:No module named' bar'`.有任何解决这个问题的方法吗? (4认同)
  • 这*几乎*对我有用,但是当我导入一个结构为带有 \__init__.py 的目录的模块时,我需要额外的一行。请参阅下面的答案-希望对某人有所帮助! (4认同)
  • 正如@SamGrondahl 所提到的,如果模块具有相对导入,这些将失败。他的[答案](/sf/answers/3527658991/)提供了python 3.5+的解决方案:python 2.7有没有类似的解决方案? (4认同)
  • 是的,我修改了我的答案以反映Python 3.4+. (3认同)
  • SourceFileLoader 并未被弃用,但 SourceFileLoader.load_module() 已被弃用。 (3认同)
  • 这是复制的想法:""import module.name as foo"",如果你的工作目录是/ path/to ?? 另外,这里的foo是什么? (2认同)
  • 我在Python 3.5.2并且我发现它****只有在文件的扩展名为.py时才有效 (2认同)
  • @Paolo Celati在Python 3.5+中你应该使用importlib.import_module(module_name).有些像这样.sys,path.append(path_to_file)module = importlib.import_module(module_name) (2认同)
  • 我可以知道为什么这里没有提到“importlib.import_module”吗? (2认同)
  • ^我最终在一个单独的 StackOverflow 问题中提出了这个问题:http://stackoverflow.com/q/41861427/1490091 (2认同)
  • `importlib.util.spec_from_file_location`不会导入不以..py` =结尾的文件( (2认同)
  • 请注意文件名中的句点,因为最新版本的 python `importlib` 模块仍然无法处理带有句点的模块文件名:https://bugs.python.org/issue38000 (2认同)
  • 有趣的是,Python 的每个新版本的代码都变得越来越长。不确定这是否真的是开发编程语言的“Pythonic 方式”:/(其他事情也一样,比如 print() 等) (2认同)
  • 使用所描述的 v3.5+ 方法会导致酸洗错误。@mforbes [此处](/sf/ask/4734201/#50395128) 上面链接的答案添加了一个额外的步骤似乎解决了这个问题:`sys.modules[spec.name] = foo` (2认同)
  • 什么是“模块名称”?我正在导入文件或文件中的某些内容。它没有命名空间。 (2认同)
  • 由于还没有人提到“pydoc”——这是我的两分钱:[pydoc::importfile()](/sf/ask/4734201/完整路径/68361215#68361215) (2认同)

Dar*_*zer 391

向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
Run Code Online (Sandbox Code Playgroud)

  • :-)也许你的问题更适合作为StackOverflow问题,而不是对答案的评论. (26认同)
  • 我们如何使用`sys.path.append`指向单个python文件而不是目录? (11认同)
  • 请注意Python缓存import语句的事实.在极少数情况下,您有两个不同的文件夹共享一个类名(classX),添加路径到sys.path,导入classX,删除路径和重复reamaining路径的方法将不起作用.Python将始终从其缓存的第一个路径加载该类.就我而言,我的目标是创建一个插件系统,其中所有插件都实现了特定的classX.我最终使用[SourceFileLoader](http://stackoverflow.com/a/67692),请注意其[弃用是有争议的](http://bugs.python.org/issue21436). (8认同)
  • 请注意,此方法允许导入的模块从同一目录导入其他模块,模块经常这样做,而接受的答案的方法则不然(至少在 3.7 上)。如果在运行时不知道模块名称,可以使用 `importlib.import_module(mod_name)` 代替显式导入,但我会在最后添加一个 `sys.path.pop()`,假设导入的代码不会尝试在使用时导入更多模块。 (4认同)
  • 对于所有试图将文件包含在其路径中的人......根据定义"shell路径是以冒号分隔的目录列表".我对python比较陌生,但是python路径也遵循我所看到的unix设计原则.如果我错了,请纠正我. (2认同)
  • python路径可以包含zip存档,"egg"(一种复杂的zip存档)等.模块可以从中导入.所以路径元素确实是_containers_文件,但它们不一定是目录. (2认同)
  • 与已接受的解决方案相比,我更喜欢此解决方案,因为它仍然适用于 Python 3.6,并且只需要 2 行简单的代码即可在另一个站点目录中找到任意数量的模块。我们的具体情况是在一个 Web 服务器上,只集中安装了几个核心包,这样用户创建的 CGI 脚本就可以从他们的用户站点目录导入。谢谢! (2认同)

Mil*_*uss 25

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

暂时

import sys
sys.path.append("/path/to/my/modules/")
import my_module
Run Code Online (Sandbox Code Playgroud)

永久性

.bashrc将以下行添加到您的文件(在Linux中)并source ~/.bashrc在终端中执行:

export PYTHONPATH="${PYTHONPATH}:/path/to/my/modules/"
Run Code Online (Sandbox Code Playgroud)

Credit/Source:saarrrr,另一个stackexchange 问题

  • 如果要在其他地方的Jupyter笔记本中进行项目生产,则此“临时”解决方案是一个很好的答案。 (2认同)

Sam*_*ahl 23

如果您的顶级模块不是文件但是打包为__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)
Run Code Online (Sandbox Code Playgroud)

如果没有这一行,当执行exec_module时,它会尝试将顶级__init__.py中的相对导入绑定到顶级模块名称 - 在本例中为"mymodule".但是"mymodule"尚未加载,因此您将收到错误"SystemError:Parent module'mymodule'未加载,无法执行相对导入".因此,您需要在加载名称之前绑定该名称.原因是相对导入系统的基本不变量:"不变量持有是如果你有sys.modules ['spam']和sys.modules ['spam.foo'](正如你在上面的导入之后那样) ),后者必须作为前者的foo属性出现" 如此处所讨论的那样.

  • 多谢!此方法支持子模块之间的相对导入。伟大的! (2认同)
  • 但什么是“mymodule”? (2认同)
  • 尽管非常规,但如果您的包入口点不是“__init__.py”,您仍然可以将其作为包导入。创建规范后包含“spec.submodule_search_locations = [os.path.dirname(MODULE_PATH)]”。您还可以通过将此值设置为“None”来将“__init__.py”视为非包(例如单个模块) (2认同)

nco*_*lan 21

听起来你不想专门导入配置文件(它有很多副作用和涉及的额外复杂性),你只想运行它,并能够访问生成的命名空间.标准库以runpy.run_path的形式专门为其提供API :

from runpy import run_path
settings = run_path("/path/to/file.py")
Run Code Online (Sandbox Code Playgroud)

该接口在Python 2.7和Python 3.2+中可用


ctc*_*rry 19

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

凌乱,但它的确有效.

configfile = '~/config.py'

import os
import sys

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

import config
Run Code Online (Sandbox Code Playgroud)


ジョー*_*ョージ 19

添加到Sebastian Rittau的答案:至少对于CPython来说,有pydoc,虽然没有正式声明,但导入文件就是它的作用:

from pydoc import importfile
module = importfile('/path/to/module.py')
Run Code Online (Sandbox Code Playgroud)

附言。为了完整起见,在撰写本文时引用了当前的实现:pydoc.py,我很高兴地说,按照xkcd 1987的精神,它没有使用问题 21436中提到的任何实现- 在至少,不是逐字逐句。

  • 这可以说是最简单的方法,并且不需要依赖项。在py3.8下测试。 (2认同)

zub*_*ber 17

你可以使用

load_source(module_name, path_to_file) 
Run Code Online (Sandbox Code Playgroud)

来自imp模块的方法.

  • 抬头说imp现在已被弃用了. (30认同)

Mad*_*ist 13

我想出了一个稍微修改过的@ 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)
Run Code Online (Sandbox Code Playgroud)

在显式中编码路径的优点SourceFileLoader机器不会试图从扩展中找出文件的类型.这意味着您可以.txt使用此方法加载类似文件的内容,但如果spec_from_file_location没有指定加载器.txt则无法执行此操作,因为它不在importlib.machinery.SOURCE_SUFFIXES.


小智 12

你的意思是加载还是导入?

您可以操作sys.path列表指定模块的路径,然后导入模块.例如,给定一个模块:

/foo/bar.py
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar
Run Code Online (Sandbox Code Playgroud)

  • `sys.path [0:0] = ['/ foo']` (11认同)
  • B/c sys.path [0] = xy覆盖第一个路径项,而path [0:0] = xy等同于path.insert(0,xy) (5认同)
  • @dom0 只需使用 `sys.path.append(...)` 即可。更清楚了。 (4认同)
  • hm path.insert为我工作,但[0:0]技巧没有. (2认同)
  • “显式优于隐式。”那么为什么不使用“ sys.path.insert(0,...)”而不是“ sys.path [0:0]”呢? (2认同)

sor*_*rin 12

下面是一些适用于所有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())
Run Code Online (Sandbox Code Playgroud)

我测试了它.它可能很难看但到目前为止是唯一一个适用于所有版本的产品.


Kum*_* KS 12

如果我们在同一个项目中有脚本,但在不同的目录中,我们可以通过以下方法解决这个问题。

在这种情况下utils.pysrc/main/util/

import sys
sys.path.append('./')

import src.main.util.utils
#or
from src.main.util.utils import json_converter # json_converter is example method
Run Code Online (Sandbox Code Playgroud)


Chr*_*way 11

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')
Run Code Online (Sandbox Code Playgroud)

  • 为什么在标准库已经解决这个问题时会编写14行错误代码?您尚未对full_path_to_module的格式或内容或os.whatever操作进行错误检查; 并使用catch-all`exce:`子句很少是一个好主意. (36认同)
  • @ChrisJohnson`标准库已经解决了这个问题,但是python有一种讨厌的习惯,就是不能向后兼容......因为经过检查的答案说3.3之前和之后有两种不同的方式.在那种情况下,我宁愿编写自己的通用功能而不是动态检查版本.是的,也许这个代码没有太好的错误保护,但它显示了一个想法(这是os.chdir(),我还没有它),基于此我可以编写更好的代码.因此+1. (11认同)
  • 如果这实际上返回了模块,那就太酷了。 (2认同)

小智 8

我相信你可以使用imp.find_module()imp.load_module()加载指定的模块.您需要将模块名称从路径中分离出来,即如果您想加载/home/mypath/mymodule.py,则需要执行以下操作:

imp.find_module('mymodule', '/home/mypath/')
Run Code Online (Sandbox Code Playgroud)

......但那应该完成工作.


小智 8

您可以使用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!
Run Code Online (Sandbox Code Playgroud)


abh*_*nyu 6

创建 Python 模块test.py

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

创建 Python 模块test_check.py

from test import Client1
from test import Client2
from test import test3
Run Code Online (Sandbox Code Playgroud)

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


fny*_*fny 6

有一个专门用于此的软件包

from thesmuggler import smuggle

# À la `import weapons`
weapons = smuggle('weapons.py')

# À la `from contraband import drugs, alcohol`
drugs, alcohol = smuggle('drugs', 'alcohol', source='contraband.py')

# À la `from contraband import drugs as dope, alcohol as booze`
dope, booze = smuggle('drugs', 'alcohol', source='contraband.py')
Run Code Online (Sandbox Code Playgroud)

它在 Python 版本(Jython 和 PyPy 也是)中进行了测试,但根据项目的大小,它可能有点过分。


小智 5

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
Run Code Online (Sandbox Code Playgroud)

这似乎使用了 Python 3.4 中未弃用的模块。我不假装明白为什么,但它似乎在程序中工作。我发现 Chris 的解决方案在命令行上有效,但在程序内部无效。