我有以下文件夹结构:
application
├── app
│ └── folder
│ └── file.py
└── app2
└── some_folder
└── some_file.py
从 some_file.py
内部,如何从 file.py
导入函数?
我试过了:
from application.app.folder.file import func_name
application
和 app1
、app2
、folder
、some_folder
都不是包,也不包含 __init__.py
,对吗?如果你要做很多这样的事情,是时候把它们做成一个包了。
注意:此答案旨在解决一个非常具体的问题。对于大多数从搜索引擎来到这里的程序员来说,这不是您要寻找的答案。通常,您会将文件构造成包(请参阅其他答案),而不是修改搜索路径。
默认情况下,您不能。导入文件时,Python 仅搜索运行入口点脚本的目录和 sys.path
,其中包括包安装目录等位置(实际上是 a little more complex,但大多数情况下都如此)。
但是,您可以在运行时添加到 Python 路径:
# some_file.py
import sys
# insert at 1, 0 is the script path (or '' in REPL)
sys.path.insert(1, '/path/to/application/app/folder')
import file
没有错:
from application.app.folder.file import func_name
只需确保 folder
还包含一个 __init__.py
,这样就可以将其作为一个包包含在内。不知道为什么其他答案谈论PYTHONPATH
。
PYTHONPATH
的情况。假设您在同一级别上有两个文件夹:A
和 B
。 A
有一个 __init.py__
。尝试从 A
中的 B
导入某些内容。
init.py
或 __init__.py
文件中有什么内容?
当模块位于并行位置时,如问题所示:
application/app2/some_folder/some_file.py
application/app2/another_folder/another_file.py
此简写使一个模块对另一个模块可见:
import sys
sys.path.append('../')
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
的父目录
cli/foo.py
能够import cli.bar
sys.path.append('.')
没问题,然后使用 from app2.some_folder.some_file import your_function
导入模块。或者,对我有用的是从根文件夹运行 python3 -m app2.another_folder.another_file
。
首先在 name-file.py 中导入 sys
import sys
其次在 name-file.py 中附加文件夹路径
sys.path.insert(0, '/the/folder/path/name-package/')
第三 在您的子目录中创建一个名为 __ init __.py 的空白文件(这告诉 Python 它是一个包)
名称文件.py
名称包 __ 初始化 __.py 名称模块.py
__ 初始化 __.py
名称-module.py
四、在name-file.py文件夹内导入模块
from name-package import name-module
sys.path.insert
-command,这也应该可以工作。因此,答案留下了问题,即使名称文件夹位于任意位置,此解决方案是否也有效。
我认为一种特别的方法是使用文档中描述的 the environment variable PYTHONPATH
:Python2、Python3
# Linux & OSX
export PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH
# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%
您的问题是 Python 正在 Python 目录中查找此文件,但没有找到它。您必须指定您正在谈论的是您所在的目录,而不是 Python 目录。
为此,您需要更改:
from application.app.folder.file import func_name
对此:
from .application.app.folder.file import func_name
通过添加点,您要在此文件夹中查找应用程序文件夹,而不是在 Python 目录中查找。
这里的答案不够清晰,这是在 Python 3.6 上测试的
使用此文件夹结构:
main.py
|
---- myfolder/myfile.py
myfile.py
包含以下内容:
def myfunc():
print('hello')
main.py
中的导入语句是:
from myfolder.myfile import myfunc
myfunc()
这将打印你好。
__init__.py
吗?
__init__.py
对我不起作用。我在 Ubuntu 18 上使用 Py 3.6.5。它适用于 Pycharm,但不适用于终端
在 Python 3.4 及更高版本中,您可以import from a source file directly (link to documentation)。这不是最简单的解决方案,但为了完整起见,我将这个答案包括在内。
这是一个例子。首先,要导入的文件,名为 foo.py
:
def announce():
print("Imported!")
导入上述文件的代码深受文档中示例的启发:
import importlib.util
def module_from_file(module_name, file_path):
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
foo = module_from_file("foo", "/path/to/foo.py")
if __name__ == "__main__":
print(foo)
print(dir(foo))
foo.announce()
输出:
<module 'foo' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!
请注意,变量名、模块名和文件名不必匹配。此代码仍然有效:
import importlib.util
def module_from_file(module_name, file_path):
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
baz = module_from_file("bar", "/path/to/foo.py")
if __name__ == "__main__":
print(baz)
print(dir(baz))
baz.announce()
输出:
<module 'bar' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!
以编程方式导入模块是在 Python 3.1 中引入的,让您可以更好地控制模块的导入方式。有关详细信息,请参阅文档。
尝试 Python 的相对导入:
from ...app.folder.file import func_name
每个前导点都是从当前目录开始的层次结构中的另一个更高级别。
问题?如果这对您不起作用,那么您可能会被许多 gotcha 的相对导入所吸引。阅读答案和评论以了解更多详情:How to fix "Attempted relative import in non-package" even with __init__.py
提示:在每个目录级别都有 __init__.py
。您可能需要从顶级目录运行的 python -m application.app2.some_folder.some_file
(不包括 .py),或者在 PYTHONPATH 中有该顶级目录。 呼!
import ..70_foo.test
),这似乎不起作用
据我所知,直接在要导入的函数的文件夹中添加一个 __init__.py
文件就可以了。
sys.path.append(tools_dir)
,不需要添加 __init__.py' file in my directory
tools_dir`
将应用程序移动到其他环境时,将 sys.path.append
与绝对路径一起使用并不理想。使用相对路径并不总是有效,因为当前工作目录取决于脚本的调用方式。
由于应用程序文件夹结构是固定的,我们可以使用 os.path
来获取我们希望导入的模块的完整路径。例如,如果这是结构:
/home/me/application/app2/some_folder/vanilla.py
/home/me/application/app2/another_folder/mango.py
假设您要导入 mango 模块。您可以在 vanilla.py 中执行以下操作:
import sys, os.path
mango_dir = (os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ '/another_folder/')
sys.path.append(mango_dir)
import mango
当然,您不需要 mango_dir 变量。
要了解其工作原理,请查看此交互式会话示例:
>>> import os
>>> mydir = '/home/me/application/app2/some_folder'
>>> newdir = os.path.abspath(os.path.join(mydir, '..'))
>>> newdir
'/home/me/application/app2'
>>> newdir = os.path.abspath(os.path.join(mydir, '..')) + '/another_folder'
>>>
>>> newdir
'/home/me/application/app2/another_folder'
>>>
并查看 os.path 文档。
还值得注意的是,使用 packages 时处理多个文件夹会更容易,因为可以使用带点的模块名称。
我面临着同样的挑战,尤其是在导入多个文件时,这就是我设法克服的方法。
import os, sys
from os.path import dirname, join, abspath
sys.path.insert(0, abspath(join(dirname(__file__), '..')))
from root_folder import file_name
bazel
运行它时,我的测试文件被移动到另一个目录,测试文件能够引用依赖项。
在 Linux 上的 python3 中为我工作
import sys
sys.path.append(pathToFolderContainingScripts)
from scriptName import functionName #scriptName without .py extension
sys.path.append("/home/linux/folder/")
— 确保不要使用快捷方式,例如 "~/folder/"
将 application
视为您的 python 项目的根目录,在 application
、app
和 folder
文件夹中创建一个空的 __init__.py
文件。然后在您的 some_file.py
中进行如下更改以获得 func_name 的定义:
import sys
sys.path.insert(0, r'/from/root/directory/application')
from application.app.folder.file import func_name ## You can also use '*' wildcard to import all the functions in file.py file.
func_name()
创建包的最佳做法是从最高级别目录中的 main_module.py
等模块运行和访问其他模块。
此结构展示了您可以通过使用顶级目录文件 main_module.py
来使用和访问子包、父包或同级包和模块。
创建并运行这些文件和文件夹以进行测试:
package/
|
|----- __init__.py (Empty file)
|------- main_module.py (Contains: import subpackage_1.module_1)
|------- module_0.py (Contains: print('module_0 at parent directory, is imported'))
|
|
|------- subpackage_1/
| |
| |----- __init__.py (Empty file)
| |----- module_1.py (Contains: print('importing other modules from module_1...')
| | import module_0
| | import subpackage_2.module_2
| | import subpackage_1.sub_subpackage_3.module_3)
| |----- photo.png
| |
| |
| |----- sub_subpackage_3/
| |
| |----- __init__.py (Empty file)
| |----- module_3.py (Contains: print('module_3 at sub directory, is imported'))
|
|------- subpackage_2/
| |
| |----- __init__.py (Empty file)
| |----- module_2.py (Contains: print('module_2 at same level directory, is imported'))
现在运行 main_module.py
输出是
>>>'importing other modules from module_1...'
'module_0 at parent directory, is imported'
'module_2 at same level directory, is imported'
'module_3 at sub directory, is imported'
打开图片和文件注意:
在包结构中,如果要访问照片,请使用最高级别目录中的绝对目录。
假设您正在运行 main_module.py
,并且您想在 module_1.py
中打开 photo.png
。
module_1.py
必须包含的是:
正确的:
image_path = 'subpackage_1/photo.png'
cv2.imread(image_path)
错误的:
image_path = 'photo.png'
cv2.imread(image_path)
尽管 module_1.py
和 photo.png
位于同一目录中。
├───root
│ ├───dir_a
│ │ ├───file_a.py
│ │ └───file_xx.py
│ ├───dir_b
│ │ ├───file_b.py
│ │ └───file_yy.py
│ ├───dir_c
│ └───dir_n
您可以将父目录添加到 PYTHONPATH
,为此,您可以使用 sys.path
中列出的“模块搜索路径”中的操作系统依赖路径。因此,您可以轻松添加父目录,如下所示:
# file_b.py
import sys
sys.path.insert(0, '..')
from dir_a.file_a import func_name
这在 Windows 上对我有用
# some_file.py on mainApp/app2
import sys
sys.path.insert(0, sys.path[0]+'\\app2')
import some_file
就我而言,我有一个要导入的课程。我的文件看起来像这样:
# /opt/path/to/code/log_helper.py
class LogHelper:
# stuff here
在我的主文件中,我通过以下方式包含了代码:
import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper
我很特别:我在 Windows 上使用 Python!
我只是完成了信息:对于 Windows 和 Linux,相对路径和绝对路径都适用于 sys.path
(我需要相对路径,因为我在几台 PC 上和不同的主目录下使用我的脚本)。
在使用 Windows 时,\
和 /
都可以用作文件名的分隔符,当然您必须将 \
加倍到 Python 字符串中,
一些有效示例:
sys.path.append('c:\\tools\\mydir')
sys.path.append('..\\mytools')
sys.path.append('c:/tools/mydir')
sys.path.append('../mytools')
(注意:我认为 /
比 \
更方便,如果它不是“Windows-native”,因为它与 Linux 兼容并且更易于编写和复制到 Windows 资源管理器)
我多次碰到同样的问题,所以我想分享我的解决方案。
Python版本:3.X
以下解决方案适用于使用 Python 版本 3.X 开发您的应用程序的人,因为 Python 2 is not supported since Jan/1/2020。
项目结构
在 python 3 中,由于 Implicit Namespace Packages,您的项目子目录中不需要 __init__.py
。见Is init.py not required for packages in Python 3.3+
Project
├── main.py
├── .gitignore
|
├── a
| └── file_a.py
|
└── b
└── file_b.py
问题陈述
在 file_b.py
中,我想在文件夹 a 下的 file_a.py
中导入一个类 A
。
解决方案
#1 快速但肮脏的方式
无需像您当前正在开发新项目那样安装软件包
使用 try catch
检查是否有错误。代码示例:
import sys
try:
# The insertion index should be 1 because index 0 is this file
sys.path.insert(1, '/absolute/path/to/folder/a') # the type of path is string
# because the system path already have the absolute path to folder a
# so it can recognize file_a.py while searching
from file_a import A
except (ModuleNotFoundError, ImportError) as e:
print("{} fileure".format(type(e)))
else:
print("Import succeeded")
#2 安装你的包
一旦你安装了你的应用程序(在这篇文章中,安装教程不包括在内)
你可以简单地
try:
from __future__ import absolute_import
# now it can reach class A of file_a.py in folder a
# by relative import
from ..a.file_a import A
except (ModuleNotFoundError, ImportError) as e:
print("{} fileure".format(type(e)))
else:
print("Import succeeded")
快乐编码!
如果从特定路径加载模块的目的是在开发自定义模块期间为您提供帮助,您可以在测试脚本的同一文件夹中创建一个指向自定义模块根目录的符号链接。对于在该文件夹中运行的任何脚本,此模块引用将优先于安装的任何其他同名模块。
我在 Linux 上对此进行了测试,但它应该适用于任何支持符号链接的现代操作系统。
这种方法的一个优点是您可以指向位于您自己的本地 SVC 分支工作副本中的模块,这可以大大简化开发周期时间并减少管理模块不同版本的故障模式。
我正在处理项目 a
,我希望用户通过 pip install a
使用以下文件列表进行安装:
.
├── setup.py
├── MANIFEST.in
└── a
├── __init__.py
├── a.py
└── b
├── __init__.py
└── b.py
安装程序.py
from setuptools import setup
setup (
name='a',
version='0.0.1',
packages=['a'],
package_data={
'a': ['b/*'],
},
)
清单文件
recursive-include b *.*
一个/初始化.py
from __future__ import absolute_import
from a.a import cats
import a.b
a/a.py
cats = 0
a/b/init.py
from __future__ import absolute_import
from a.b.b import dogs
a/b/b.py
dogs = 1
我通过使用 MANIFEST.in
从目录中运行以下命令来安装模块:
python setup.py install
然后,从我的文件系统 /moustache/armwrestle
上完全不同的位置,我能够运行:
import a
dir(a)
这证实了 a.cats
确实等于 0 并且 a.b.dogs
确实等于 1,正如预期的那样。
不只是执行 import ...
,而是执行以下操作:
from <MySubFolder> import <MyFile>
MyFile 位于 MySubFolder 中。
以防有人仍在寻找解决方案。这对我有用。
Python 将包含您启动的脚本的文件夹添加到 PYTHONPATH,因此如果您运行
python application/app2/some_folder/some_file.py
只有文件夹 application/app2/some_folder 被添加到路径中(不是您在其中执行命令的基本目录)。相反,将文件作为模块运行并在 some_folder 目录中添加 __init__.py 。
python -m application.app2.some_folder.some_file
这会将基本目录添加到 python 路径,然后可以通过非相对导入访问类。
哇,我没想到会花这么多时间在这上面。以下对我有用:
操作系统:Windows 10
蟒蛇:v3.10.0
注意:由于我是 Python v3.10.0,因此我没有使用 __init__.py
文件,这对我来说无论如何都不起作用。
application
├── app
│ └── folder
│ └── file.py
└── app2
└── some_folder
└── some_file.py
WY Hsu's 第一个解决方案对我有用。为了清楚起见,我用绝对文件参考重新发布了它:
import sys
sys.path.insert(1, 'C:\\Users\\<Your Username>\\application')
import app2.some_folder.some_file
some_file.hello_world()
替代解决方案:但是,这也对我有用:
import sys
sys.path.append( '.' )
import app2.some_folder.some_file
some_file.hello_world()
虽然,我不明白为什么它会起作用。我认为点是对当前目录的引用。但是,当打印出当前文件夹的路径时,当前目录已经列在顶部:
for path in sys.path:
print(path)
希望有人可以在评论中说明为什么这样做有效。不过,我也希望它对某人有所帮助。
sys.path.append('..')
再建立一个分支并且它有效!但仍然显示 CWD 的路径
下面的代码以 Python 版本安全的方式导入由其路径给出的 Python 脚本,无论它位于何处:
def import_module_by_path(path):
name = os.path.splitext(os.path.basename(path))[0]
if sys.version_info[0] == 2:
# Python 2
import imp
return imp.load_source(name, path)
elif sys.version_info[:2] <= (3, 4):
# Python 3, version <= 3.4
from importlib.machinery import SourceFileLoader
return SourceFileLoader(name, path).load_module()
else:
# Python 3, after 3.4
import importlib.util
spec = importlib.util.spec_from_file_location(name, path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
我在 psutils.test.__init__.py
的第 1042 行的 psutils 代码库中找到了这个(最近一次提交到 09.10.2020)。
使用示例:
script = "/home/username/Documents/some_script.py"
some_module = import_module_by_path(script)
print(some_module.foo())
重要警告:该模块将被视为顶级;从其中的父包中进行的任何相对导入都将失败。
spec_from_file_location
开始在 loader 对象中保存文件的根路径(如果给定了相对路径),但返回的数据是相同的。还使用 python 3.10 进行了测试——与 3.8.10 完全相同的行为。两种方法都可以正常工作。
您可以使用 importlib 将模块导入到您希望使用如下字符串从文件夹中导入模块的位置:
import importlib
scriptName = 'Snake'
script = importlib.import_module('Scripts\\.%s' % scriptName)
此示例有一个 main.py,它是上面的代码,然后是一个名为 Scripts 的文件夹,然后您可以通过更改 scriptName
变量从该文件夹中调用您需要的任何内容。然后您可以使用 script
来引用此模块。例如,如果我在 Snake 模块中有一个名为 Hello()
的函数,您可以通过以下方式运行此函数:
script.Hello()
我已经在 Python 3.6 中对此进行了测试
我通常会为要导入的模块创建一个符号链接。符号链接确保 Python 解释器可以在当前目录中找到模块(您将另一个模块导入到的脚本);稍后当您的工作结束时,您可以删除符号链接。此外,您应该忽略 .gitignore 中的符号链接,这样您就不会意外地将符号链接模块提交到您的存储库。这种方法甚至可以让您成功地使用与您正在执行的脚本平行的模块。
ln -s ~/path/to/original/module/my_module ~/symlink/inside/the/destination/directory/my_module
如果您有多个文件夹和子文件夹,您始终可以从主目录导入任何类或模块。
例如:项目的树形结构
Project
├── main.py
├── .gitignore
|
├── src
├────model
| └── user_model.py
|────controller
└── user_controller.py
现在,如果你想从 main.py 文件中的 user_model.py 导入“UserModel”类,你可以使用:
from src.model.user_model.py import UserModel
此外,您可以使用同一行在 user_controller.py 文件中导入相同的类:
from src.model.user_model.py import UserModel
总的来说,您可以参考主项目目录来导入项目目录内任何 python 文件中的类和文件。
__init__.py
来实现吗?
这个问题可能是由于 Pycharm
我在使用 Pycharm 时遇到了同样的问题。我有这个项目结构
skylake\
backend\
apps\
example.py
configuration\
settings.py
frontend\
...some_stuff
和 example.py 中的代码 from configuration import settings
引发导入错误
问题是当我打开Pycharm时,它认为skylake是根路径并运行了这段代码
sys.path.extend(['D:\\projects\\skylake', 'D:/projects/skylake'])
https://i.stack.imgur.com/6yaVi.png
它解决了我的问题
sys.path.append('/path/to/application/app/folder')
更干净 imosys.path
返回list
,而不是deque
,将list
转换为deque
并返回是愚蠢的。