从父文件夹导入模块

Ram*_*hum 552 python directory module path python-import

我正在运行Python 2.5.

这是我的文件夹树:

ptdraft/
  nib.py
  simulations/
    life/
      life.py
Run Code Online (Sandbox Code Playgroud)

(我也在__init__.py每个文件夹中,为了便于阅读,这里省略了)

如何nib从模块内导入life模块?我希望有可能不用修补sys.path.

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

f3l*_*lix 574

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

from ... import nib
Run Code Online (Sandbox Code Playgroud)

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

编辑:添加了另一个点'.' 上两个包

  • 更准确地说,你需要一个`__init __.py`文件. (286认同)
  • `ValueError:在非包中尝试相对导入 (147认同)
  • 尝试相对导入超出顶级包 (28认同)
  • @endolith:你必须在一个包中,即你必须有一个__init__.py文件. (16认同)
  • 另请参阅以下答案,因为添加`__init __.py`不是您唯一需要做的事情:http://stackoverflow.com/questions/11536764/attempted-relative-import-in-non-package-even-with -init-PY/27876800#27876800 (16认同)
  • `ImportError:尝试在没有已知父包的情况下进行相对导入` (11认同)
  • 我们怎样才能先上升再下降呢? (3认同)
  • @btk 像哈森的答案那样进行绝对导入没有任何问题。这在 Django 中已经完成了数百次。这是绝对可以接受的。在某些情况下,进行相对导入会更好、更短,因此请随意互换使用这些方法。 (2认同)
  • 为了*甚至*更精确,你需要一个`__init __.py`文件. (2认同)
  • @karadeniz好吧,这并不更精确。实际上,您省略了在父目录中需要`__init __。py`的事实。 (2认同)
  • 总结[相关问题的答案](http://stackoverflow.com/questions/11536764/attempted-relative-import-in-non-package-even-with-init-py/27876800#27876800):对于相对或绝对导入工作(没有令人讨厌的路径黑客,或安装为可编辑包的麻烦),你只需要使用“python -m”运行,例如在本例中为“python -m ptdraft.nib”。所有导入都会得到正确解析,请参阅 [PEP338](https://www.python.org/dev/peps/pep-0338/)。 (2认同)

Rem*_*emi 257

相对导入(如在from .. import mymodule)中仅在包中起作用.要导入当前模块的父目录中的"mymodule":

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) 

import mymodule
Run Code Online (Sandbox Code Playgroud)

编辑:__file__并不总是给出属性.而不是使用os.path.abspath(__file__)我现在建议使用inspect模块来检索当前文件的文件名(和路径)

  • 更短的东西:`sys.path.insert(1,os.path.join(sys.path [0],'..'))` (85认同)
  • 有什么理由避免使用sys.path.append()而不是插入? (7认同)
  • @JHolta如果您没有处理包裹,那么您的包装是最好的解决方案. (4认同)
  • @JHolta好主意。sys.path.insert(1,os.path.realpath(os.path.pardir))也可以工作。 (3认同)
  • @Tyler - 在其他地方然后是`parentdir`可能很重要,但是在`sys.path`中已经指定的其中一个路径中,还有另一个名为'mymodule'的模块.插入`parentdir`作为`sys.path`列表的第一个元素确保将导入来自`parentdir`的模块. (2认同)
  • @SpeedCoder5实际上,如果您的工作目录与sys.path[0]不同,则需要使用sys.path[0]。请参阅/sf/ask/49984441/#comment102640654_28712742。 (2认同)

np8*_*np8 113

关于从兄弟套餐进口的问题,我也发了类似的答案.你可以在这里看到它.以下是使用Python 3.6.5(Anaconda,conda 4.5.1),Windows 10计算机进行测试的.

没有sys.path -hacks或相对导入的解决方案

建立

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

.
??? ptdraft
    ??? __init__.py
    ??? nib.py
    ??? simulations
        ??? __init__.py
        ??? life
            ??? __init__.py
            ??? life.py
Run Code Online (Sandbox Code Playgroud)

我调用.根文件夹,在我的情况下它位于C:\tmp\test_imports.

脚步

1)将setup.py添加到根文件夹

内容setup.py可以简单

from setuptools import setup, find_packages

setup(name='myproject', version='1.0', packages=find_packages())
Run Code Online (Sandbox Code Playgroud)

基本上"任何"setup.py都可以.这只是一个极小的工作示例.

2)使用虚拟环境

如果您熟悉虚拟环境,请激活一个,然后跳到下一步.虚拟环境的使用并非绝对必要,但从长远来看,它们将真正帮助您(当您有超过1个项目正在进行时......).最基本的步骤是(在根文件夹中运行)

  • 创建虚拟环境
    • python -m venv venv
  • 激活虚拟环境
    • . /venv/bin/activate(Linux)或./venv/Scripts/activate(Win)

要了解更多相关信息,只需谷歌"python虚拟环境教程"或类似内容.除了创建,激活和停用之外,您可能永远不需要任何其他命令.

制作并激活虚拟环境后,控制台应在括号中指定虚拟环境的名称

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>
Run Code Online (Sandbox Code Playgroud)

3)将项目pip安装在可编辑状态

安装您的顶级包myproject使用pip.诀窍是-e在安装时使用标志.这样,它以可编辑状态安装,对.py文件所做的所有编辑将自动包含在已安装的包中.

在根目录中,运行

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

您还可以看到它是使用安装的 pip freeze

(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///C:/tmp/test_imports
Installing collected packages: myproject
  Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0
Run Code Online (Sandbox Code Playgroud)

4)通过附加mainfolder到每个进口进口

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


示例用法

nib.py

def function_from_nib():
    print('I am the return value from function_from_nib!')
Run Code Online (Sandbox Code Playgroud)

life.py

from ptdraft.nib import function_from_nib

if __name__ == '__main__':
    function_from_nib()
Run Code Online (Sandbox Code Playgroud)

运行life.py

(venv) PS C:\tmp\test_imports> python .\ptdraft\simulations\life\life.py
I am the return value from function_from_nib!
Run Code Online (Sandbox Code Playgroud)

  • 2020 年在 python 3.8 上导入模块还需要这么大量的工作吗?我简直不敢相信这有多复杂。 (21认同)
  • 为什么让 python 导入变得更加复杂?我们必须编写一个安装文件、一个虚拟环境、一个 pip 安装吗?我的天啊! (13认同)
  • 这个答案是“全面的”,但它把简单的任务误认为是复杂的。无论如何,你都应该设置虚拟环境(以避免干扰 Python 系统),并且安装项目是一项基本技能。答案实际上可以归结为:“为您的项目安装包,然后包根目录将位于路径上,以便您可以正常使用‘import’”。设置完成后,相对(只要不超过包根目录)和绝对(从包根目录开始)导入都可以工作。 (6认同)
  • @KarlKnechtel 这不是一个简单的代表复杂的任务。在 2022 年,这很复杂,坦率地说,这很糟糕。Python 应该从小型本地项目中的 __init__.py 文件生成可用模块名称列表,并使其可用于导入,但事实并非如此。如果您只想引用不同目录中几个不同文件中的函数,那么将整个项目打包听起来有些过分了。 (6认同)
  • 对于较小的项目,路径黑客肯定有效,而对于较大的项目,这个答案对我帮助很大!当你有 7 个目录和 50 多个文件时,你能否想象跟踪你在代码中编辑 sys.path 的位置,并将其推送到 git 并让你的队友重用你的代码变成一场灾难!谢谢np8!我相信这是长期来看最方便的最佳解决方案 (4认同)
  • @HomeroBarrocasSEsmeraldo好的问题。PYTHONPATH env var保持不变。它安装在[site-packages](/sf/answers/3684722191/)中,该文件已经在sys.path中,并且最终用户通常将代码安装在该位置。这比sys.path骇客要好(并且您可以在该类路径骇客中包括使用PYTHONPATH),因为这意味着开发中的代码对您的行为应与对其他用户的行为相同。相反,在使用路径修改时,将以不同的方式解析import语句。 (3认同)
  • @EmilTerman您可以通过在其自己的行上添加“-e .”将其添加到“requirements.txt”中。我把它放在“requirements.txt”的顶部。 (3认同)
  • 另外,为什么这比使用sys.path方法更好或更优雅? (2认同)
  • 如何将其包含在“requirements.txt”中? (2认同)
  • 到目前为止,这是我读过的最好的解决方案。这将创建一个到当前目录的链接,因此对代码的所有更新都将直接反映到您的工作目录中。如果需要删除该条目,请手动删除egg文件,并从dist-packages(如果使用pip,则为site-packages)中的easy-install中删除该条目。 (2认同)
  • 也可以使用“pip uninstall package-name”删除包,并且可以从“pip list”读取包名称。 (2认同)
  • [PEP 517](https://www.python.org/dev/peps/pep-0517/) 不支持可编辑安装,如 [setuptools](https://setuptools.readthedocs.io/en/latest/ 上所述setuptools.html?highlight=517#setup-cfg-only-projects) 。将目录添加到 python 查找路径的最安全、最灵活的方法是什么?我想避免 sys.path 和 pythonpath hack,到目前为止这是最好的答案。 (2认同)
  • @KarlKnechtel 哈哈,如果我们想引用格言“如果实现很难解释,那就是一个坏主意。” 让我们在默认情况下使命名空间更容易:)。现在他们很痛苦。我还没有看到一分钟的制作方法。如果它们很简单,就会有更多的人谈论它们并使用它们。为什么我被迫使用虚拟环境?在 Java、Typescript、Dart 中有必要吗?并不真地.. (2认同)

has*_*sen 108

似乎问题与父目录中的模块或类似的东西无关.

您需要将包含的目录添加ptdraft到PYTHONPATH

你说这import nib与你合作,这可能意味着你把ptdraft自己(不是它的父母)添加到了PYTHONPATH.

  • 如果您正在使用许多永远不会重用的包,则必须将所有内容添加到 pythonpath 并不是一个好的解决方案。 (51认同)
  • 对我来说这个答案已经解决了。然而,为了更明确,过程是 `import sys` 然后 `sys.path.append("..\<parent_folder>")` (17认同)
  • @JasonCheng:那为什么它们是**包**? (3认同)
  • @JasonCheng,您不必导出修改后的 PYTHONPATH 或 shell 配置文件,您还可以选择仅针对对 python 的单次调用进行设置(因此仅在每次执行时进行修改),或者您可以将其放入某种 .env 文件(基于每个文件夹)。 (2认同)

Ric*_*llo 54

如果将模块文件夹添加到PYTHONPATH不起作用,您可以在程序中修改Python解释器搜索要导入的模块的sys.path列表,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
Run Code Online (Sandbox Code Playgroud)

  • 你的答案很好,但可能并不总是有效,也不是很便携.如果程序被移动到新位置,则必须编辑`/ path/to/ptdraft`.有一些解决方案可以解决文件的当前目录,并从父文件夹中导入它. (6认同)

Shi*_*ero 45

您可以在sys.path中列出的"模块搜索路径"中使用OS依赖路径.因此,您可以轻松添加如下所示的父目录

import sys
sys.path.insert(0,'..')
Run Code Online (Sandbox Code Playgroud)

如果要添加父父目录,

sys.path.insert(0,'../..')
Run Code Online (Sandbox Code Playgroud)

  • 这是相对于当前目录的,甚至不一定是包含主脚本的目录。 (13认同)

小智 41

不太了解python 2.
在python 3中,父文件夹可以添加如下:

import sys 
sys.path.append('..')
Run Code Online (Sandbox Code Playgroud)

...然后一个人可以从中导入模块

  • 这只适用于当前工作目录是"'.."导致带有相关模块的目录. (9认同)

Edw*_*ard 29

这是一个简单的答案,因此您可以看到它的工作原理,小型和跨平台.
它仅使用内置模块(os,sysinspect),所以应该工作
在任何操作系统(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)
Run Code Online (Sandbox Code Playgroud)

对于比此更少的行,将第二行替换为import os.path as path, sys, inspect,在(第3行)的开头
添加并删除第一行. - 但是这会导入所有模块,因此可能需要更多时间,内存和资源.inspect.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.
Run Code Online (Sandbox Code Playgroud)

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

from inspect import getsourcefile  
from os.path import abspath  
Run Code Online (Sandbox Code Playgroud)

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

abspath(getsourcefile(lambda:0))
Run Code Online (Sandbox Code Playgroud)

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

在代码中导入模块后, 当添加的文件夹具有与 稍后在程序中导入的另一个模块同名的模块时,最好sys.path.pop(0)在新行上运行.您需要删除导入前添加的列表项,而不是其他路径. 如果您的程序没有导入其他模块,那么不删除文件路径是安全的,因为 在程序结束(或重新启动Python shell)之后,任何编辑都会消失.



sys.path

关于文件名变量的注释

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

一些例子在那里不起作用(从报价这个堆栈溢出的问题):

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

  • py2exe没有__file__属性,但有一个解决方法
  • 从IDLE运行时execute()没有__file__属性
  • OS X 10.6我得到的地方 NameError: global name '__file__' is not defined


Mik*_*ike 28

这是更通用的解决方案,包括sys.path中的父目录(适用于我):

import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
Run Code Online (Sandbox Code Playgroud)

  • 正如 StackOverflow 用户经常提到的,这个答案使用了 `__file__` 变量,它可能是 **不可靠的**(并不总是完整的文件路径,不适用于每个操作系统等)。将答案更改为不包含它会导致更少的问题并且更具交叉兼容性。有关更多信息,请参阅 /sf/answers/2347240171/。 (2认同)

Vin*_*san 17

两行最简单的解决方案

import os, sys
sys.path.insert(0, os.getcwd()) 
Run Code Online (Sandbox Code Playgroud)

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

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

python child_module1/child_script.py
Run Code Online (Sandbox Code Playgroud)

  • 这是唯一对我有用并且最适合我的用例的方法:从测试文件夹导入主要方法 (2认同)

Tho*_*s R 16

在 Jupyter 笔记本中

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

%cd ..
import nib
Run Code Online (Sandbox Code Playgroud)

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

我在 Linux 和 Windows 7 上使用 Anaconda3 对其进行了测试。

  • 在导入后添加 `%cd -` 是否有意义,这样笔记本的当前目录保持不变。 (5认同)

小智 13

我认为您可以在该特定示例中尝试此操作,但在 Python\xc2\xa03.6.3 中:

\n

在此输入图像描述

\n


小智 12

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

.
??? my_application
    ??? alembic
        ??? env.py
    ??? app
        ??? __init__.py
        ??? db
Run Code Online (Sandbox Code Playgroud)
import os
import sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)
Run Code Online (Sandbox Code Playgroud)

  • 完全修改“sys.path”通常是一个坏主意。您需要某种方式(*例如*,`PYTHONPATH`)让您的库可以被*其他*代码访问(否则它不是真正的库);一直使用它! (2认同)

zvi*_*iad 9

对我来说,访问父目录的最短和最喜欢的oneliner是:

sys.path.append(os.path.dirname(os.getcwd()))
Run Code Online (Sandbox Code Playgroud)

要么:

sys.path.insert(1, os.path.dirname(os.getcwd()))
Run Code Online (Sandbox Code Playgroud)

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

实际上,在我看来,Python项目架构应该以子目录中没有一个模块将使用父目录中的任何模块的方式完成.如果发生这样的事情,重新考虑项目树是值得的.

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

  • +1代表必须重组项目的可能性(而不是对问题进行整顿) (2认同)

And*_*han 7

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

  • 从其他目录执行时,这将不起作用. (7认同)

Ere*_*evi 6

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

import nib
Run Code Online (Sandbox Code Playgroud)

  • 我不同意。这是一个不可移植的解决方案,还将文件系统层与 python 源代码混合在一起。 (5认同)

小智 6

上述解决方案也很好。解决此问题的另一种方法是

如果要从顶层目录导入任何内容。然后,

from ...module_name import *
Run Code Online (Sandbox Code Playgroud)

另外,如果要从父目录导入任何模块。然后,

from ..module_name import *
Run Code Online (Sandbox Code Playgroud)

另外,如果要从父目录导入任何模块。然后,

from ...module_name.another_module import *
Run Code Online (Sandbox Code Playgroud)

这样,您可以根据需要导入任何特定方法。

  • 这似乎应该是这样,但由于某种原因,这与上面的 `sys.path.append` 黑客的工作方式不同,我无法像这样导入我的库。这是我在开始谷歌之前首先尝试做的:)好吧,就是这个“ValueError:尝试相对导入超出顶级包” (12认同)
  • 我做了一些测试,看来 `from ... import name` 并不意味着“从顶层导入名称”,而是“从父级的父级导入名称”。我创建了一个由九个目录组成的嵌套目录结构“a1/.../a9”,其中每个目录都包含一个“__init__.py”,使用“from”导入下一个目录。导入 next_dir`。最后一个目录有一个包含“from ....... import b”的文件,然后导入“a1/a2/a3/b”。(使用Python 3.8.2测试) (2认同)

Tho*_*s R 6

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

首先我使用了sys.path.append('..')类似的解决方案。如果您导入的文件本身是使用sys.path.append('..').

然后我决定始终附加 Git 存储库的根目录。在一行中它看起来像这样:

sys.path.append(git.Repo('.', search_parent_directories=True).working_tree_dir)
Run Code Online (Sandbox Code Playgroud)

或者更详细地这样:

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)
Run Code Online (Sandbox Code Playgroud)

对于最初的问题:根据存储库的根目录是什么,导入将是

import ptdraft.nib
Run Code Online (Sandbox Code Playgroud)

或者

import nib
Run Code Online (Sandbox Code Playgroud)


YFP*_*YFP 5

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

import os,sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0,parentdir)
Run Code Online (Sandbox Code Playgroud)

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


小智 5

当不在包含__init__.py文件的包环境中时,pathlib库(包含在> = Python 3.4中)使得将父目录的路径附加到PYTHONPATH非常简洁直观:

import sys
from pathlib import Path
sys.path.append(str(Path('.').absolute().parent))
Run Code Online (Sandbox Code Playgroud)


tjk*_*tjk 5

虽然原作者可能不再寻找解决方案,但为了完整性,有一个简单的解决方案。它将life.py作为一个模块运行,如下所示:

cd ptdraft
python -m simulations.life.life
Run Code Online (Sandbox Code Playgroud)

这样你就可以从nib.py导入任何东西,因为ptdraft目录在路径中。


归档时间:

查看次数:

590377 次

最近记录:

6 年,1 月 前