从父文件夹导入模块

2024-11-26 08:37:00
admin
原创
174
摘要:问题描述:我正在运行 Python 2.5。这是我的文件夹树:ptdraft/ nib.py simulations/ life/ life.py (每个文件夹中都有__init__.py,为了方便阅读这里省略)如何nib从模块内部导入模块life?我希望可以不用修改 sys.pa...

问题描述:

我正在运行 Python 2.5。

这是我的文件夹树:

ptdraft/
  nib.py
  simulations/
    life/
      life.py

(每个文件夹中都有__init__.py,为了方便阅读这里省略)

如何nib从模块内部导入模块life?我希望可以不用修改 sys.path 来实现。

注意:正在运行的主模块位于ptdraft文件夹中。


解决方案 1:

您可以使用相对导入(Python >= 2.5):

from ... import nib

(Python 2.5 中的新功能)PEP 328:绝对导入和相对导入

解决方案 2:

我还针对有关从兄弟包导入的问题发布了类似的答案。您可以在此处看到它。

无需sys.path破解的解决方案

概括

  • 将代码包装到一个文件夹中(例如packaged_stuff

  • 创建pyproject.toml(旧替代方案setup.py:)

  • Pip 使用以下可编辑状态安装包pip install -e <myproject_folder>

  • 导入使用from packaged_stuff.modulename import function_name

设置

我假设文件夹结构与问题相同

.
└── ptdraft
    ├── __init__.py
    ├── nib.py
    └── simulations
        ├── __init__.py
        └── life
            ├── __init__.py
            └── life.py

我称之为.根文件夹,在我的情况下,它位于C: mp est_imports

步骤


pyproject.toml1)在根文件夹中添加

的内容pyproject.toml可以简单

[project]
name = "ptdraft"
version = "0.1.0"
description = "My small project"

[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.2,<4"]

基本上“任何”有效方法pyproject.toml都可以。这只是一个最小工作示例,使用 flit 作为构建后端。


2)使用虚拟环境

如果您熟悉虚拟环境,请激活一个,然后跳到下一步。虚拟环境的使用不是绝对必要的,但从长远来看,它们确实会对您有所帮助(当您有多个正在进行的项目时……)。最基本的步骤是(在根文件夹中运行)

  • 创建虚拟环境

    • python -m venv venv

  • 激活虚拟环境

    • . venv/bin/activate(Linux) 或./venv/Scripts/activate(Win)

  • 停用虚拟环境

    • deactivate(Linux)

要了解更多信息,只需在 Google 上搜索“python virtualenv tutorial”或类似内容。除了创建、激活和停用之外,您可能不需要任何其他命令。

创建并激活虚拟环境后,控制台应在括号中显示虚拟环境的名称

PS C:    mp    est_imports> python -m venv venv
PS C:    mp    est_imports> .envScriptsactivate
(venv) PS C:    mp    est_imports>

3)pip 安装你的项目到可编辑状态

使用安装顶级包(此处ptdraftpip。诀窍是-e在安装时使用标志。这样它就会以可编辑状态安装,并且对 .py 文件所做的所有编辑都将自动包含在已安装的包中。请注意,-e带有 pyproject.toml 的标志需要pip 21.3 或更新版本。

在根目录中运行

pip install -e .(注意点,它代表“当前目录”)

您还可以使用以下命令安装它pip freeze

(venv) PS C:    mp    est_imports> pip install -e .
Obtaining file:///home/user/projects/ptdraft
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build editable ... done
  Preparing editable metadata (pyproject.toml) ... done
....
Successfully built ptdraft
Installing collected packages: ptdraft
Successfully installed ptdraft-0.1.0
(venv) PS C:    mp    est_imports> pip freeze
ptdraft==0.1.0

4)通过mainfolder在每个导入前面添加导入

在这个例子中,mainfolder将是ptdraft。这样做的好处是你不会遇到与其他模块名称(来自python标准库或第三方模块)的名称冲突。


示例用法

nib.py

def function_from_nib():
    print('I am the return value from function_from_nib!')

生活.py

from ptdraft.nib import function_from_nib

if __name__ == '__main__':
    function_from_nib()

运行 life.py

(venv) PS C:    mp    est_imports> python .ptdraftsimulationslifelife.py
I am the return value from function_from_nib!

解决方案 3:

相对导入(如from .. import mymodule)仅在包中有效。要导入当前模块父目录中的“mymodule”,请执行以下操作:

import os
import sys
import inspect

currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0, parentdir) 

import mymodule

注意:__file__属性并非总是给出的。os.path.abspath(__file__)我建议使用检查模块来检索当前文件的文件名(和路径),而不是使用。

解决方案 4:

看起来该问题与模块位于父目录中或类似的东西无关。

您需要将包含以下内容的目录添加ptdraft到 PYTHONPATH

您说这import nib对您有用,这可能意味着您将ptdraft它自己(而不是它的父级)添加到了 PYTHONPATH。

解决方案 5:

您可以在sys.path中列出的“模块搜索路径”中使用与操作系统相关的路径。

因此您可以轻松地添加父目录,如下所示:

import sys
sys.path.insert(0, '..')

如果你想添加父级目录,

sys.path.insert(0, '../..')

这在Python 2和Python 3中都有效。

解决方案 6:

我对Python 2不太了解。

在Python 3中,可以按如下方式添加父文件夹:

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

...然后就可以从中导入模块。

解决方案 7:

如果将模块文件夹添加到 PYTHONPATH 不起作用,您可以修改程序中的sys.path列表,Python 解释器在其中搜索要导入的模块,Python 文档说:

当导入名为spam 的模块时,解释器首先搜索具有该名称的内置模块。如果未找到,它会在变量 sys.path 给出的目录列表中搜索名为spam.py的文件。sys.path 从以下位置初始化:

  • 包含输入脚本的目录(或当前目录)。

  • PYTHONPATH(目录名称列表,语法与 shell 变量 PATH 相同)。

  • 安装相关的默认值。

初始化后,Python 程序可以修改sys.path。包含正在运行的脚本的目录将放在搜索路径的开头,位于标准库路径之前。这意味着将加载该目录中的脚本,而不是库目录中同名的模块。除非有意替换,否则这是一个错误。

了解了这一点,您可以在程序中执行以下操作:

import sys
# Add the ptdraft folder path to the sys.path list
sys.path.append('/path/to/ptdraft/')

# Now you can import your module
from ptdraft import nib
# Or just
import ptdraft

解决方案 8:

pathlib 库(包含在 >= Python 3.4 中)使得将父目录的路径附加到 PYTHONPATH 变得非常简洁直观:

import sys
from pathlib import Path
sys.path.append(str(Path(__file__).absolute().parent))

解决方案 9:

这是一个简单的答案,您可以看到它是如何工作的,小巧且跨平台。它仅使用内置模块(ossysinspect),因此它应该可以在任何操作系统 (OS) 上运行,因为 Python 是为此设计的。

答案的简短代码——更少的行数和变量

from inspect import getsourcefile
import os.path as path, sys
current_dir = path.dirname(path.abspath(getsourcefile(lambda:0)))
sys.path.insert(0, current_dir[:current_dir.rfind(path.sep)])
import my_module  # Replace "my_module" here with the module name.
sys.path.pop(0)

对于少于此行数的情况,将第二行替换为import os.path as path, sys, inspectinspect.在(第 3 行)开头添加getsourcefile并删除第一行。

  • 但是这会导入所有模块,因此可能需要更多的时间、内存和资源。

我的答案的代码(较长的版本

from inspect import getsourcefile
import os.path
import sys

current_path = os.path.abspath(getsourcefile(lambda:0))
current_dir = os.path.dirname(current_path)
parent_dir = current_dir[:current_dir.rfind(os.path.sep)]

sys.path.insert(0, parent_dir)

import my_module  # Replace "my_module" here with the module name.

它使用 Stack Overflow 答案“如何获取 Python 中当前执行文件的路径?”中的示例,使用内置工具查找正在运行的代码的源(文件名)。

from inspect import getsourcefile
from os.path import abspath

接下来,无论您想从哪里找到源文件,只需使用:

abspath(getsourcefile(lambda:0))

我的代码将文件路径添加到Python 路径列表sys.path中,
因为这允许 Python 从该文件夹导入模块。

sys.path.pop(0)在代码中导入模块后,如果添加的文件夹中有一个模块与程序中稍后导入的另一个模块同名,则最好在新行上运行。您需要删除导入前添加的列表项,而不是其他路径。

如果您的程序没有导入其他模块,那么不删除文件路径是安全的,因为在程序结束后(或重新启动 Python shell),所做的任何编辑都会sys.path消失。

关于文件名变量的注释

我的答案不使用__file__变量来获取正在运行的代码的文件路径/文件名,因为这里的用户经常将其描述为不可靠的。 您不应该在其他人使用的程序中使用它从父文件夹导入模块

一些不起作用的例子(引用自这个Stack Overflow 问题):

•在某些平台上找不到。有时它不是完整的文件路径

  • py2exe没有__file__属性,但有一个解决方法

  • 当您从 IDLE 运行时,execute()没有__file__属性

  • OS X 10.6 我在哪里得到NameError: global name '__file__' is not defined

解决方案 10:

在Jupyter Notebook中(使用JupyterLab或 Jupyter Notebook 打开)

只要你在 Jupyter Notebook 中工作,这个简短的解决方案可能会有用:

%cd ..
import nib

即使没有__init__.py文件它也能工作。

我使用 Linux 和 Windows 7 上的Anaconda 3对其进行了测试。

解决方案 11:

这是一个更通用的解决方案,将父目录包含到sys.path中(对我有用):

import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))

解决方案 12:

我发现以下方法适用于从脚本的父目录导入包。在示例中,我想env.pyapp.db包中导入函数。

.
└── my_application
    └── alembic
        └── env.py
    └── app
        ├── __init__.py
        └── db
import os
import sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)

解决方案 13:

前面提到的解决方案也可以。此问题的另一种解决方案是:

如果你想从顶层目录导入任何内容。那么,

from ...module_name import *

另外,如果你想从父目录导入任何模块。那么,

from ..module_name import *

另外,如果你想从父目录导入任何模块。那么,

from ...module_name.another_module import *

这样,如果您愿意,您可以导入任何特定的方法。

解决方案 14:

两行最简单的解决方案

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

如果父级是您的工作目录,并且您想从子脚本调用另一个子模块。

您可以在任何脚本中从父目录导入所有子模块并将其执行为

python child_module1/child_script.py

解决方案 15:

我认为您可以在该特定示例中尝试此操作,但在 Python 3.6.3 中:

在此处输入图片描述

解决方案 16:

为了完整性,有一个简单的解决方案。就是将life.py作为模块运行,如下所示:

cd ptdraft
python -m simulations.life.life

这样,您可以从nib.py导入任何内容,因为ptdraft目录位于路径中。

解决方案 17:

对于我来说,访问父目录最短也是我最喜欢的一行代码是:

sys.path.append(os.path.dirname(os.getcwd()))

或者:

sys.path.insert(1, os.path.dirname(os.getcwd()))

os.getcwd() 返回当前工作目录的名称,os.path.dirname(directory_name) 返回传递的目录名称。

实际上,我认为 Python 项目架构应该这样设计:子目录中的任何模块都不会使用父目录中的任何模块。如果发生这种情况,就值得重新考虑项目树。

另一种方法是将父目录添加到 PYTHONPATH 系统环境变量。

解决方案 18:

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

解决方案 19:

这与过去的答案风格相同,但行数更少:P

import os, sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0, parentdir)

文件返回您正在工作的位置。

解决方案 20:

我有一个专门针对 Git 存储库的解决方案。

一开始我使用了sys.path.append('..')类似的解决方案。如果您导入的文件本身也是使用 导入文件,那么这尤其会造成问题sys.path.append('..')

然后我决定始终附加 Git 存储库的根目录。一行代码如下所示:

sys.path.append(git.Repo('.', search_parent_directories=True).working_tree_dir)

或者更详细地如下:

import os
import sys
import git
def get_main_git_root(path):
    main_repo_root_dir = git.Repo(path, search_parent_directories=True).working_tree_dir
    return main_repo_root_dir
main_repo_root_dir = get_main_git_root('.')
sys.path.append(main_repo_root_dir)

对于原始问题:根据存储库的根目录,导入将是

import ptdraft.nib

或者

import nib

解决方案 21:

在 Linux 系统中,你可以从“life”文件夹创建到 nib.py 文件的软链接。然后,你可以简单地将其导入,如下所示:

import nib

解决方案 22:

我们的文件夹结构:

/myproject
  project_using_ptdraft/
    main.py
  ptdraft/
    __init__.py
    nib.py
    simulations/
      __init__.py
      life/
        __init__.py
        life.py

我对此的理解是采用以包为中心的视图。包根是ptdraft,因为它是最顶层,包含__init__.py

包内的所有文件都可以使用绝对路径(相对于包根目录)进行导入,例如在 中life.py,我们只需:

import ptdraft.nib

但是,为了运行life.py包开发/测试目的,python life.py我们需要使用:

cd /myproject
python -m ptdraft.simulations.life.life

请注意,此时我们根本不需要摆弄任何路径。


进一步的混乱是当我们完成ptdraft包时,我们想要在驱动程序脚本中使用它,而该脚本必然在ptdraft包文件夹之外,也就是说project_using_ptdraft/main.py,我们需要摆弄路径:

import sys
sys.path.append("/myproject")  # folder that contains ptdraft
import ptdraft
import ptdraft.simulations

并用来python main.py运行脚本而没有问题。

有用的链接:

  • Python 幕后花絮 #11:Python 导入系统如何工作(了解如何__init__.py使用)

  • 运行包初始化代码

  • 在 Python 3 中,从同一包内和包外导入模块

  • 即使使用 __init__.py,如何修复“尝试在非包中相对导入”

解决方案 23:

使用 pathlib.Path

import sys
from pathlib import Path
sys.path.append(str(Path(f"{__file__}").parent.parent))
import my_module

解决方案 24:

使用库。创建一个名为 nib 的库,使用 setup.py 安装它,让它驻留在 site-packages 中,您的问题就解决了。您不必将您创建的所有东西都塞进一个包中。将其分解成几个部分。

解决方案 25:

我遇到了一个问题,我必须导入一个 Flask 应用程序,该应用程序的导入也需要导入单独文件夹中的文件。这部分使用了Remi 的答案,但假设我们有一个如下所示的存储库:

.
└── service
    └── misc
        └── categories.csv
    └── test
        └── app_test.py
    app.py
    pipeline.py

然后,在从文件导入应用程序对象之前app.py,我们将目录更改到上一级,因此当我们导入应用程序(导入pipeline.py)时,我们还可以读取各种文件,例如 csv 文件。

import os,sys,inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir)

os.chdir('../')
from app import app

导入 Flask 应用程序后,您可以使用os.chdir('./test'),这样您的工作目录就不会改变。

解决方案 26:

我觉得你其实不需要导入父模块。假设在 nib.py 中你有 func1() 和 data1,你需要在 life.py 中使用

nib.py

import simulations.life.life as life
def func1():
   pass
data1 = {}
life.share(func1, data1)

生活.py

func1 = data1 = None

def share(*args):
   global func1, data1
   func1, data1 = args

现在,您可以访问 life.py 中的 func1 和数据。当然,在尝试使用它们之前,您必须小心地在 life.py 中填充它们,

解决方案 27:

在删除一些系统路径 hack 之后,我认为添加

我喜欢的解决方案。

注意:这是一个框架挑战 - 没有必要在代码中进行。

假设有一棵树,

project
└── pkg
    └── test.py

其中test.py包含

import sys, json; print(json.dumps(sys.path, indent=2)) 

使用路径执行仅包含包目录

python pkg/test.py
[
  "/project/pkg",
 ...
]

但使用模块参数包括项目目录

python -m pkg.test
[
  "/project",
  ...
]

现在,所有导入都可以从项目目录绝对导入。无需进一步的欺骗。

解决方案 28:

在开发新模块时我遇到了很多困难,但最终还是选择了将目录附加到 os.path 来解决问题,所以我所做的就是在我的项目中的每个目录中添加以下内容: __init__.py

import os, sys
pardir = ""
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(current_dir))
while pardir != "p":
    parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir))
    pardir = os.path.basename(parent_dir)
    current_dir = parent_dir
    sys.path.append(parent_dir)

其中“p”是我的项目文件夹,例如:c:/p/ 此解决方案在向其他人开放项目时不会对您有所帮助,您必须(像我一样)找到另一个解决方案(我可能会在完成代码后在此处发布我如何从这里开始)但至少这会帮助您更快地开始原型设计。我知道这个解决方案已经讨论过了,我只是想分享我的复制粘贴解决方案。我的结构是这样的:

p
|---project
    |---src
    |    |----__init__.py
    |    |----module.py
    |---tests
    |    |----__init__.py
    |    |----tests.py
    |---__init__.py

在所有地方都有一个__init__.py并没有帮助我从 p/project/tests/tests.py 运行我的 tests.py

到目前为止,我尝试了很多解决方案,但大多数都让我感到害怕或根本不起作用。为什么用 Python 会这么复杂?我选择的解决方案也没有让我满意。

解决方案 29:

首先是代码:

from pathlib import Path
import sys
ptdraft = str(Path(sys.path[0]).parent.parent)
sys.path.insert(0, ptdraft)
import nib

根据文档,

sys.path 从以下位置初始化:

  • 包含输入脚本的目录(未指定文件时为当前目录)。

  • PYTHONPATH(目录名称列表,语法与 shell 变量 PATH 相同)。

  • 安装相关的默认值(按照惯例包括站点包目录,由站点模块处理)。

因此,sys.path像这样的列表["script/directory/path", "pythonpath/element/1", "pythonpath/element/2", ..., ".../lib/python3.x", ..., ".../lib/python3.x/site-packages"]

脚本目录的路径始终位于该列表的 0 位置。由于 OP 有nib.py他想要导入两个目录的模块 ( ),因此他可以使用 来获取其目录Path(sys.path[0].parent.parent)sys.path仅适用于字符串,因此结果必须包装在 中str()

下一步是将此路径插入到 中的正确位置sys.path。仅当目录中的任何地方都可以找到具有相同名称的多个模块时,位置才重要sys.path。对此有三种合理的选择:

  • .insert(0, ptdraft)插入到列表的开头,因此具有最高优先级。

  • .insert(1, ptdraft)将(祖父)父目录放在脚本目录之后和目录之前PYTHONPATH

  • .append(ptdraft)将(祖)父目录放在最后作为后备。

解决方案 30:

这是对我来说最简单的解决方案:

from ptdraft import nib
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1043  
  IPD(Integrated Product Development,集成产品开发)是一种系统化的产品开发方法论,旨在通过跨职能团队的协作,优化产品开发的效率和质量。IPD流程强调从市场需求出发,通过并行工程、跨部门协作和阶段性评审,确保产品从概念到上市的每个环节都高效且可控。随着敏捷开发方法的普及,越来越多的企业开始...
华为IPD流程   41  
  随着企业产品开发复杂度的提升以及市场需求的快速变化,传统的产品开发模式逐渐显现出局限性。集成产品开发(IPD)流程与敏捷开发(Agile Development)作为两种主流的开发方法论,分别从系统化管理和快速响应需求的角度为企业提供了解决方案。然而,单独使用其中一种方法往往无法完全满足企业在效率、质量和创新上的多重需...
华为IPD流程   35  
  华为IPD(Integrated Product Development,集成产品开发)流程是华为公司成功的关键因素之一。它不仅帮助华为在技术上实现了快速创新,还通过市场导向确保了产品的商业成功。IPD流程通过整合技术与市场双驱动,实现了从需求定义到产品交付的全生命周期管理。这种模式不仅提高了产品的开发效率,还降低了市...
IPD流程中PDCP是什么意思   32  
  在研发领域,集成产品开发(IPD)流程已经成为企业提升创新效率和市场竞争力的重要手段。然而,资源分配的不合理往往是制约IPD流程效率的关键因素之一。无论是人力资源、财务资源还是技术资源,如何高效分配直接关系到项目的成功与否。优化资源分配不仅能够缩短产品开发周期,还能降低研发成本,提升产品的市场竞争力。因此,掌握资源分配...
IPD流程中CDCP   34  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用