当文件名包含句点时,如何引用Python包

55

我正在使用Django,有一个名为models.admin.py的文件,我想在models.py中实现以下想法:

from "models.admin" import *

然而,由于使用了双引号,我遇到了语法错误。但是如果我只是这样做

from models.admin import *

然后我收到了 "ImportError: No module named admin" 的错误信息。

有没有办法从一个带有点号的python文件中导入模块?


1
从技术上讲,那是一个模块,而不是一个包。 - Cerin
7个回答

42

实际上,你是可以导入一个无效名称的模块的。但你需要使用imp来实现,例如,假设文件名为models.admin.py,你可以这样做:

import imp
with open('models.admin.py', 'rb') as fp:
    models_admin = imp.load_module(
        'models_admin', fp, 'models.admin.py',
        ('.py', 'rb', imp.PY_SOURCE)
    )

在开始使用之前,请阅读关于imp.find_moduleimp.load_module的文档。


4
不良实践?impimport__import__ 的机制。它更加灵活,但需要更多的代码。虽然没有什么神奇的地方。 - Cat Plus Plus
10
机器码是万物背后的机制。更灵活,你明白我要说什么了吗? :) - shylent
3
如果你真的需要使用它,显然你需要三思而后行,但我并不认为使用它是非常错误的。;) 它被用于生产环境——例如,请参见Trac - http://trac.edgewall.org/browser/trunk/trac/loader.py。 - Cat Plus Plus
4
记录一下,当我意识到这会很复杂时,我从来没有给我的文件命名。但是由于出于好奇搜索后无法找到答案,我决定将答案(import imp)和被接受的惯例的声音(不要使用它)都存档以供未来的搜寻者参考。 :) - Alexander Bird
2
我认为重要的一点是,根据 Python 风格指南(http://www.python.org/dev/peps/pep-0008/),这是一个无效的模块名称。然而,在 OP 的情况下,如果无法更改模块名称,则此解决方案似乎是合理的,因为它使用了良好记录的接口。换句话说,未来的 Python 次要版本不太可能更改此行为。如果解决方案涉及未记录的功能,则情况就不同了,我会同意 shylent 的观点。 - haridsv
显示剩余4条评论

15
如果你真的想要的话,可以使用 imp 模块导入一个带有不寻常文件名的模块(例如,在“.py”之前包含“.”的文件名)。
>>> import imp
>>> a_b = imp.load_source('a.b', 'a.b.py')
>>> a_b.x
"I was defined in a.b.py!"

然而,这通常是个坏主意。更有可能的情况是你想要使用包,此时你应该创建一个名为 "a" 的目录,其中包含一个名为 "b.py" 的文件;然后 "import a.b" 将会加载 a/b.py。


2
load_source 自 1.5 版本起已经过时。 - Cat Plus Plus
True,虽然不过时的做法要冗长得多:a_b = imp.load_module('a.b', open('a.b.py'), os.path.abspath('a.b.py'), ('.py', 'r', imp.PY_SOURCE)) - Edward Loper

4

假设目录结构如下:

C:.
│   script.py
│   
└───Parent
    └───Child
        ├───1.1
        │       main.py
        │       
        └───1.2

假设您想在script.py中导入main.py

您的main.py如下所示:

def my_function():
  print("Hello from a function")

你的 script.py 看起来像下面这样

from os import path
import importlib
from os.path import dirname
import sys
import importlib.util


def getPath():
    # your logic to get to the path
    return path.join(dirname(__file__),'Parent','Child','1.1','main.py')

file_path = getPath() 
module_name = 'main'

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

#call functions like this
module.my_function()

请查看此代码片段

,它与IT技术有关。


4
文件名为models/admin.py。(来源
也就是说,它应该在名为models的目录中命名为admin.py
然后,你可以使用from models.admin import *进行导入,假设它在你的Python路径中。

3

如果一个Python文件的名称包含句点(或问号、感叹号等),则无法将其作为模块导入。Python模块的名称(不包括.py)必须是有效的Python名称(即可用作变量名)。


你手头有这方面的参考资料吗? - Trent
4
参考文献是Python语法规范(https://docs.python.org/2/reference/grammar.html)。然而,这个规范仅指定了import语句允许使用什么--正如上面被接受的答案所指出的那样,通过使用底层的import机制技术上可以绕过这个限制。 - Gabriel Reid
长名称由多个单词组成时,最佳模式是什么?给一个包命名,并创建一个名为"awesome.project"的文件夹会导致问题,而"awesomeProject"似乎是一种反模式。不确定是否正确理解了这个规则:https://www.python.org/dev/peps/pep-0423/#id87 - Eric Burel
实际上你可以,参见@SciTech Enthusiast的回答。 - run_the_race

0
在我的情况下,我正在使用 grafanalib,根据 doc 文档,文件名必须为 xx.dashboard.py。然而,我想要导入该文件以简化上传步骤。
当我使用 import imp 时,我会得到警告:

imp 模块已被弃用,建议使用 importlib,并计划在 Python 3.12 中删除。有关替代用法,请参阅模块的文档。

这是一个使用 importlibpathlib 的简单演示: foo.bar.pymain.py 在同一个文件夹中。
# foo.bar.py

num = 42

# main.py

import importlib.machinery
import pathlib

module = importlib.machinery.SourceFileLoader(
    "foo_bar",
    pathlib.Path(__file__).parent.joinpath("foo.bar.py").resolve().as_posix(),
).load_module()
print(module.num)  # 42

-1

你在import语句中引用的不是文件,而是模块和包。

请阅读文档,它们对此问题非常清楚。

无论如何,由于你正在使用django,通常的方法不起作用。如果你想将模型放在单独的文件中,而不是models.py中,你必须采取额外的步骤,例如在这里中概述的步骤。

编辑:
好吧,我真的不知道提问者在提到“admin”时意味着什么,以及它是否与django的管理界面有关。我的观点仍然成立。


1
一个模块就是一个文件。引用你所提到的文档:为了支持这一点,Python 有一种方法可以将定义放在一个文件中,并在脚本或解释器的交互实例中使用它们。这样的文件被称为模块; - Alexander Bird

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接