繁体   English   中英

从不同文件夹导入文件

[英]Importing files from different folder

我有这个文件夹结构:

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

如何从file.py中的 file.py 导入some_file.py 我试过了:

from application.app.folder.file import func_name

但它不起作用。

注意:此答案旨在解决一个非常具体的问题。 对于大多数从搜索引擎来到这里的程序员来说,这不是您要寻找的答案。 通常,您会将文件构造成包(请参阅其他答案),而不是修改搜索路径。


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

但是,您可以在运行时添加到 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

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

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

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

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

首先在 name-file.py 中导入 sys

 import sys

其次在 name-file.py 中附加文件夹路径

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

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

  • 名称文件.py
  • 名称包
    • __ 初始化 __.py
    • 名称-module.py

、在name-file.py文件夹内导入模块

from name-package import name-module

我认为一种特别的方法是使用文档中描述 的环境变量PYTHONPATHPython2Python3

# 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()

这将打印hello

在 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 中引入的,让您可以更好地控制模块的导入方式。 有关详细信息,请参阅文档。

尝试 Python 的相对导入:

from ...app.folder.file import func_name

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


问题? 如果这对您不起作用,那么您可能会被许多 gotcha 的相对导入所吸引。 阅读答案和评论以获取更多详细信息: 即使使用 __init__.py,如何修复“尝试在非包中进行相对导入”

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

据我所知,直接在要导入的函数的文件夹中添加一个__init__.py文件就可以了。

将应用程序移动到其他环境时,将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

在 Linux 上的 python3 中为我工作

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

application视为 python 项目的根目录,在applicationappfolder文件夹中创建一个空的__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.pyphoto.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 中开发您的应用程序的人,因为自 2020 年 1 月 1 日起不支持 Python 2

项目结构

在 python 3 中,由于Implicit Namespace Packages ,您的项目子目录中不需要__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 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 分支工作副本中的模块,这可以大大简化开发周期时间并减少管理模块不同版本的故障模式。

我正在处理我希望用户通过pip install a项目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/初始化.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 而abdogs确实等于 1,正如预期的那样。

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

from <MySubFolder> import <MyFile>

MyFile 位于 MySubFolder 中。

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

操作系统:Windows 10

蟒蛇:v3.10.0

注意:由于我是 Python v3.10.0,我没有使用__init__.py文件,这对我来说无论如何都不起作用。

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

WY Hsu 的第一个解决方案对我有用。 为了清楚起见,我已经用绝对文件参考重新发布了它:

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)

希望有人可以在评论中说明为什么这样做有效。 不过,我也希望它对某人有所帮助。

下面的代码以 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 的代码库中发现了这一点,位于psutils.test.__init__.py的第 1042 psutils.test.__init__.py最近一次提交为 09.10.2020 )。

使用示例:

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

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

python -m application.app2.some_folder.some_file

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

您可以使用 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 文件中的类和文件。

这个问题可能是由于 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'])

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

它解决了我的问题

您可以按 f5 刷新 Python shell,或转到 Run-> Run Module。 这样,您不必更改目录即可从文件中读取内容。 Python 会自动更改目录。 但是如果你想在 Python Shell 中处理来自不同目录的不同文件,那么你可以更改 sys 中的目录,正如 Cameron之前所说。

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

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

import sys
sys.insert(1, /path) 

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

最后,我猜对我有用的不是pythonic,而是:

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

所以:

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

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

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

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

import sys
import os
sys.path.insert(0, os.getcwd())

import application.app.folder.file as file

您可以使用pippip install -e. 命令。 您必须在项目目录的根目录中创建一个名为setup.py的文件,其中包含以下内容:

from setuptools import find_packages, setup

setup(
    name='src',
    packages=find_packages(),
    version='0.1.0',
    description='my_project',
    author='author',
    license='MIT',
)

然后输入pip install -e. 在项目的根目录中。 这将使所有目录都以其名称作为模块被调用。 例如,如果您的根目录包含子目录module1module2 ,每个子目录中都有脚本,您将能够使用以下命令从任何子目录访问它们,对于module1

import module1.script1 as script1

以防万一有人仍然需要解决方案并且没有在上面的答案中找到解决方案。 这对我有用:

我有这个文件夹结构:

a
└── b
    ├── c1
    |   └── d
    |       └── a_script.py
    └── c2
        └── a_lib.py

我需要将a_lib.py包含在a_script.py中。 这就是我解决无法识别c2的错误的方法:

import sys
from pathlib import Path
path_scripts = Path(__file__).resolve().parents[2]
sys.path.append(str(path_scripts))
from c2 import a_lib

只需使用 os 模块中的更改目录功能:

os.chdir("Here new director")

比你可以正常导入更多信息

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM