从不同文件夹导入文件

我有这个文件夹结构:

application├── app│   └── folder│       └── file.py└── app2└── some_folder└── some_file.py

如何从file.py导入一个函数,从some_file.py?我试过了:

from application.app.folder.file import func_name

但它不起作用。

2861452 次浏览

注意:这个答案是针对一个非常具体的问题。对于大多数从搜索引擎来到这里的程序员来说,这不是你想要的答案。通常,你会将文件结构化为包(参见其他答案),而不是修改搜索路径。


默认情况下,您不能。当导入文件时,Python只搜索入口点脚本运行的目录和sys.path,其中包括包安装目录等位置(实际上是稍微复杂一点,但这涵盖了大多数情况)。

但是,您可以在运行时添加到Python路径:

    # some_file.pyimport sys# caution: path[0] is reserved for script path (or '' in REPL)sys.path.insert(1, '/path/to/application/app/folder')
import file

我认为一个特别的方法是使用环境变量#0,如留档中所述:python2python3

# Linux & OSXexport PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH
# Windowsset PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%

没有什么错:

from application.app.folder.file import func_name

只要确保folder也包含__init__.py,这允许它作为一个包包含。不知道为什么其他答案谈论PYTHONPATH

据我所知,直接在您要导入的函数的文件夹中添加一个__init__.py文件就可以完成这项工作。

application作为python项目的根目录,在applicationappfolder文件夹中创建一个空的__init__.py文件。然后在some_file.py中进行如下更改以获取func_name的定义:

import syssys.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()

如果从特定路径加载模块的目的是帮助您开发自定义模块,则可以在测试脚本的同一文件夹中创建指向自定义模块根目录的符号链接。对于在该文件夹中运行的任何脚本,此模块引用将优先于安装的同名的任何其他模块。

我在Linux上测试了这个,但它应该适用于任何支持符号链接的现代操作系统。

这种方法的一个优点是,您可以指向位于您自己的本地SVC分支工作副本中的模块,这可以大大简化开发周期时间并减少管理不同版本模块的失败模式。

这对我在Windows上工作

# some_file.py on mainApp/app2import syssys.path.insert(0, sys.path[0]+'\\app2')
import some_file

在linux上的python3中为我工作

import syssys.path.append(pathToFolderContainingScripts)from scriptName import functionName #scriptName without .py extension

当模块处于并行位置时,如问题所示:

application/app2/some_folder/some_file.pyapplication/app2/another_folder/another_file.py

这种简写使一个模块对另一个模块可见:

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

您的问题是Python正在Python目录中查找此文件而没有找到它。您必须指定您谈论的是您所在的目录而不是Python目录。

要做到这一点,你改变这个:

from application.app.folder.file import func_name

对此:

from .application.app.folder.file import func_name

通过添加点,您说在这个文件夹中查找应用程序文件夹,而不是在Python目录中查找。

我很特别:我在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原生”,因为它Linux兼容并且更容易编写和复制到Windows资源管理器)

您可以通过按f5刷新Python shell,或者转到Run->Run模块。这样您就不必更改目录来从文件中读取内容。Python会自动更改目录。但是如果您想使用Python Shell中不同目录的不同文件,那么您可以更改sys中的目录,正如卡梅伦所说早些时候。

将应用程序移动到其他环境时,将sys.path.append与绝对路径一起使用并不理想。使用相对路径并不总是有效,因为当前工作目录取决于脚本的调用方式。

由于应用程序文件夹结构是固定的,我们可以使用os.path来获取我们要导入的模块的完整路径。例如,如果这是结构:

/home/me/application/app2/some_folder/vanilla.py/home/me/application/app2/another_folder/mango.py

假设您想导入芒果模块。您可以在vanilla.py中执行以下操作:

import sys, os.pathmango_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留档。

同样值得注意的是,使用可以更轻松地处理多个文件夹,因为可以使用虚线模块名称。

在我的例子中,我有一个类要导入。我的文件如下所示:

# /opt/path/to/code/log_helper.pyclass LogHelper:# stuff here

在我的主文件中,我通过以下方式包含代码:

import syssys.path.append("/opt/path/to/code/")from log_helper import LogHelper

尝试Python的相对导入:

from ...app.folder.file import func_name

每个前导点都是从当前目录开始的层次结构中的另一个更高级别。


问题?如果这对你不起作用,那么你可能会被许多问题的相对导入所困扰。阅读答案和评论了解更多详情:如何修复“在非包中尝试相对导入”即使使用__init__. py

提示:每个目录级别都有__init__.py。您可能需要从顶级目录运行的python -m application.app2.some_folder.some_file(离开. py),或者在您的PYTHONPATH中拥有该顶级目录。呼!

这里的答案缺乏明确性,这是在Python 3.6上测试的

使用此文件夹结构:

main.py|---- myfolder/myfile.py

其中myfile.py包含内容:

def myfunc():print('hello')

main.py中的导入语句是:

from myfolder.myfile import myfuncmyfunc()

这将打印你好

第一导入系统name-file.py

 import sys

第二添加文件夹路径name-file.py

sys.path.insert(0, '/the/folder/path/name-package/')

第三在子目录中创建一个名为__init__. py的空白文件(这告诉Python它是一个包)

  • name-file.py
  • 名称包
    • __init__. py
    • name-module.py

第四导入文件夹中的模块name-file.py

from name-package import name-module

所以我只是右键单击我的IDE,添加了一个新的folder,并想知道为什么我不能从中导入。后来我意识到我必须右键单击并创建一个Python包,而不是经典的文件系统文件夹。或者像其他答案中提到的那样添加__init__.py(这使得python将文件系统文件夹视为一个包)的事后方法。在这里添加这个答案,以防有人走这条路线。

在Python 3.4及更高版本中,您可以直接从源文件导入(链接到留档)。这不是最简单的解决方案,但为了完整性,我包含了这个答案。

下面是一个例子。首先,要导入的文件,名为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中引入了以编程方式导入模块,使您可以更好地控制模块的导入方式。有关更多信息,请参阅留档。

我面临着同样的挑战,特别是在导入多个文件时,这就是我设法克服它的方式。

import os, sys
from os.path import dirname, join, abspathsys.path.insert(0, abspath(join(dirname(__file__), '..')))
from root_folder import file_name

我正在开发项目a,我希望用户通过pip install a安装以下文件列表:

.├── setup.py├── MANIFEST.in└── a├── __init__.py├── a.py└── b├── __init__.py└── b.py

setup.py

from setuptools import setup
setup (name='a',version='0.0.1',packages=['a'],package_data={'a': ['b/*'],},)

MANIFEST.in

recursive-include b *.*

A/init.py

from __future__ import absolute_import
from a.a import catsimport 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 adir(a)

这证实了a.cats确实等于0,a.b.dogs确实等于1,正如预期的那样。

您可以使用导入lib导入要从文件夹中导入模块的模块,使用字符串如下:

import importlib
scriptName = 'Snake'
script = importlib.import_module('Scripts\\.%s' % scriptName)

这个例子有一个main.py,它是上面的代码,然后是一个名为脚本的文件夹,然后您可以通过更改scriptName变量从该文件夹中调用任何您需要的内容。然后您可以使用script来引用此模块。例如,如果我在Snake模块中有一个名为Hello()的函数,您可以通过这样做来运行此函数:

script.Hello()

我已经在Python 3.6中测试过了

我多次遇到同样的问题,所以我想分享我的解决方案。

Python版本:3. X

以下解决方案适用于在Python版本3. X中开发您的应用程序的人,因为自2020年1月1日起不再支持Python 2

项目结构

在python 3中,由于隐式命名空间包,您的项目子目录中不需要__init__.py。请参阅Python 3.3+中的包不需要init. py吗

Project├── main.py├── .gitignore|├── a|   └── file_a.py|└── b└── file_b.py

问题陈述

file_b.py中,我想在文件夹a下的file_a.py中导入一个类A

解决方案

#1快速但肮脏的方式

无需安装包,就像您当前正在开发一个新项目一样

使用try catch检查错误。代码示例:

import systry:# The insertion index should be 1 because index 0 is this filesys.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 searchingfrom file_a import Aexcept (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 importfrom ..a.file_a import Aexcept (ModuleNotFoundError, ImportError) as e:print("{} fileure".format(type(e)))else:print("Import succeeded")

编码愉快!

而不是做一个import ...,这样做:

from <MySubFolder> import <MyFile>

MyFile位于MySubFolder中。

这些问题我已经遇到很多次了。我经常来到这个页面。在我的最后一个问题中,我必须从固定目录运行server,但每当调试时,我都想从不同的子目录运行。

import syssys.insert(1, /path)

不是对我有用,因为在不同的模块中,我必须读取不同的*. csv文件,这些文件都在同一个目录中。

最后,对我有用的不是Pythonic,我猜,但是:

我使用了if __main__在我要调试的模块上面,它从不同于通常的路径运行。

所以:

# On top of the module, instead of on the bottomimport osif __name__ == '__main__':os.chdir('/path/for/the/regularly/run/directory')

我通常创建一个指向我要导入的模块的符号链接。符号链接确保Python解释器可以在当前目录(您正在导入其他模块的脚本)中找到模块;稍后,当您的工作结束时,您可以删除符号链接。此外,您应该忽略. gitignore中的符号链接,这样,您就不会意外地将符号链接的模块提交到存储库。这种方法甚至可以让您成功地使用与您正在执行的脚本并行的模块。

ln -s ~/path/to/original/module/my_module ~/symlink/inside/the/destination/directory/my_module

创建包的最佳实践可以是从最高级别目录中的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.pyphoto.png在同一个目录中。

如果您有多个文件夹和子文件夹,则始终可以从主目录导入任何类或模块。

例如:项目的树形结构

Project├── main.py├── .gitignore|├── src├────model|    └── user_model.py|────controller└── user_controller.py

现在,如果你想从main.py文件中的user_model.py导入用户模型类,你可以使用:

from src.model.user_model.py import UserModel

此外,您可以使用同一行在user_controller.py文件中导入相同的类:

from src.model.user_model.py import UserModel

总的来说,您可以引用主项目目录来导入项目目录中任何python文件中的类和文件。

只需使用os模块中的更改dir函数:

os.chdir("Here new director")

您可以正常导入更多信息

下面的代码以Python版本安全的方式导入其路径给定的Python脚本,无论它位于何处:

def import_module_by_path(path):name = os.path.splitext(os.path.basename(path))[0]if sys.version_info[0] == 2:# Python 2import impreturn imp.load_source(name, path)elif sys.version_info[:2] <= (3, 4):# Python 3, version <= 3.4from importlib.machinery import SourceFileLoaderreturn SourceFileLoader(name, path).load_module()else:# Python 3, after 3.4import importlib.utilspec = importlib.util.spec_from_file_location(name, path)mod = importlib.util.module_from_spec(spec)spec.loader.exec_module(mod)return mod

我在psutils的代码库中找到了这个,在#0截至2020年10月9日的最新提交)的第1042行。

使用示例:

script = "/home/username/Documents/some_script.py"some_module = import_module_by_path(script)print(some_module.foo())

重要警告:模块将被视为顶级;从其父包的任何相对导入都将失败。

以防有人仍在寻找解决方案。这对我很有效。

Python将包含您启动的脚本的文件夹添加到PYTHONPATH,因此如果您运行

python application/app2/some_folder/some_file.py

只有文件夹apps/app2/some_folder被添加到路径中(而不是执行命令的基目录)。相反,将文件作为模块运行,并在some_folder目录中添加__init__. py。

python -m application.app2.some_folder.some_file

这将把基本目录添加到python路径中,然后类将通过非相对导入访问。

├───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 syssys.path.insert(0, '..')
from dir_a.file_a import func_name

哇,我没想到会花这么多时间在这上面。以下对我有用:

os:Windows 10

python:业务线

说明:因为我是Python v3.10.0,所以我不使用__init__.py文件,无论如何这对我都不起作用。

application├── app│   └── folder│       └── file.py└── app2└── some_folder└── some_file.py

WY Hsu的第一个解决方案对我有效。为了清楚起见,我用绝对文件引用转发了它:

import syssys.path.insert(1, 'C:\\Users\\<Your Username>\\application')import app2.some_folder.some_file
some_file.hello_world()

替代解决方案:不过,这对我也有效:

import syssys.path.append( '.' )import app2.some_folder.some_file
some_file.hello_world()

虽然,我不明白它为什么有效。我以为点是对当前目录的引用。然而,当打印出当前文件夹的路径时,当前目录已经列在顶部:

for path in sys.path:print(path)

希望有人能在评论中解释清楚为什么这是有效的。尽管如此,我也希望它能帮助到某人。

如果您只想运行脚本而不是实际导入脚本,则exec命令将完成这项工作

exec(open('/full/or/relative/path').read())

这个问题可能是由于PyCharm

我在使用PyCharm时也遇到了同样的问题。我有这个项目结构

skylake\backend\apps\example.pyconfiguration\settings.pyfrontend\...some_stuff

代码from configuration import settingsexample.py导入错误

问题是,当我打开PyCharm时,它认为skylake是根路径并运行此代码

sys.path.extend(['D:\\projects\\skylake', 'D:/projects/skylake'])

为了解决这个问题,我只是将后端目录标记为源根目录输入图片描述

它解决了我的问题

我的解决方案的人。谁拥有所有必要的__init__.py在包中,但导入仍然不起作用。

import sysimport ossys.path.insert(0, os.getcwd())
import application.app.folder.file as file