首页 文章

从父文件夹导入模块

提问于
浏览
425

我正在运行Python 2.5 .

这是我的文件夹树:

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

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

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

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

16 回答

  • 22

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

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

    关于从兄弟套餐进口的问题,我也发了类似的答案 . 你可以看到here . 以下是使用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
    

    我将 . 称为根文件夹,在我的情况下,它位于 C:\tmp\test_imports 中 .

    步骤

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

    setup.py 的内容可以很简单

    from setuptools import setup, find_packages
    
    setup(name='myproject', version='1.0', packages=find_packages())
    

    基本上“任何”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>
    

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

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

    在根目录中,运行

    pip install -e . (注意点,它代表"current directory")

    您还可以看到它是使用 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
    

    4)通过将mainfolder附加到每个导入来导入

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


    示例用法

    nib.py

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

    life.py

    from ptdraft.nib import function_from_nib
    
    if __name__ == '__main__':
        function_from_nib()
    

    正在运行life.py

    (venv) PS C:\tmp\test_imports> python .\ptdraft\simulations\life\life.py
    I am the return value from function_from_nib!
    
  • 90

    这是一个简单的答案,因此您可以看到它的工作原理,小型和跨平台 .
    它只使用内置模块( 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, inspect 替换第二行,
    getsourcefile (第3行)的开头添加 inspect. 并删除第一行 .

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

    我的答案代码(更长版本)

    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回答How do I get the path of the current executed file in Python?中的示例来查找使用内置工具运行代码的源(文件名) .

    来自inspect import getsourcefile
    来自os.path导入abspath
    接下来,无论你想从哪里找到源文件,只需使用:abspath(getsourcefile(lambda:0))

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

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

    关于文件名变量的注释

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

    Some examples where it doesn't work (quote from this Stack Overflow question):

    •它在某些平台上找到了can't be•有时isn't the full file path

    py2exe没有__file__属性,但有一个解决方法当您从IDLE运行execute()时,没有__file__属性OS X 10.6我得到NameError:全局名称'file'未定义

  • 21

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

  • 198

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

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

    你说 import nib 和你一起工作,这可能意味着你把 ptdraft 本身(不是它的父亲)添加到了PYTHONPATH .

  • 23

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

    from ... import nib
    

    (What’s New in Python 2.5) PEP 328: Absolute and Relative Imports

    EDIT :添加了另一个点'.'以上升两个包

  • 6

    相对导入(如 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
    

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

  • 28

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

    导入名为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
    
  • 6

    您可以在"module search path"中使用"module search path"中的OS依赖路径 . 因此,您可以轻松添加如下所示的父目录

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

    如果要添加父父目录,

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

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

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

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

  • 34

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

    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系统环境变量中 .

  • 1

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

    import sys
    from pathlib import Path
    sys.path.append(str(Path('.').absolute().parent))
    
  • 4

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

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

    file返回您正在使用的位置

  • 501

    与图书馆合作 . 创建一个名为nib的库,使用setup.py安装它,让它驻留在site-packages中,你的问题就解决了 . 您不必将所有内容都包含在一个包中 . 把它分解成碎片 .

  • 1

    上面提到的解决方案也很好 . 这个问题的另一个解决方案是

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

    from ...module_name import *
    

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

    from ..module_name import *
    

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

    from ...module_name.another_module import *
    

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

  • -1

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

    import nib
    

相关问题