如何导入上面目录中的Python类?

use*_*975 178 python directory module python-import

我想继承一个位于当前目录之上的文件中的类.

是否可以相对导入该文件?

gim*_*mel 164

在包层次结构中,使用两个点,因为import语句 doc说:

指定要导入的模块时,不必指定模块的绝对名称.当模块或包包含在另一个包中时,可以在同一顶层包中进行相对导入,而不必提及包名.通过在指定模块或包中使用前导点后from,可以指定在不指定确切名称的情况下遍历当前包层次结构的高度.一个前导点表示存在导入模块的当前包.两个点表示一个包级别.三个点是两个级别,等等.因此,如果您from . import modpkg包中的模块执行,那么您将最终导入pkg.mod.如果from ..subpkg2 import mod从内部执行,pkg.subpkg1则会导入pkg.subpkg2.mod.相对进口的规范包含在PEP 328中.

PEP 328涉及绝对/相对进口.

  • 结果导致 ImportError:尝试在没有已知父包的情况下进行相对导入 (19认同)
  • up1 = os.path.abspath('..')sys.path.insert(0,up1) (4认同)
  • 这将触发错误ValueError:尝试相对顶级包进行相对导入 (2认同)

Sep*_*ero 100

import sys
sys.path.append("..") # Adds higher directory to python modules path.
Run Code Online (Sandbox Code Playgroud)

  • 粗略地说,只有应用程序的PWD值 - 它的当前目标,才是父项的子项才有效.因此,即使它起作用,多种系统性变化也可能使它失效. (7认同)
  • 这对我有用。添加这个后,我可以直接导入父模块,不需要使用“..”。 (2认同)

Ale*_*lli 75

@gimel的答案是正确的,如果你能保证他提到的包层次结构.如果你不能 - 如果你的真实需要是你所表达的,完全依赖于目录而与包装没有任何必要的关系 - 那么你需要努力__file__找出父目录(几个os.path.dirname电话会做; - ),然后(如果该目录尚未上sys.path)预先准备暂时插入所说的最开始的目录sys.path,__import__,除去说了一遍DIR -杂乱的工作的确,但是,"当你必须,你必须"(和Pyhon努力永远不要阻止程序员做必须做的事情 - 正如ISO C标准在其序言中的"Spirit of C"部分所说的那样! - ).

这是一个可能适合您的示例:

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

import module_in_parent_dir
Run Code Online (Sandbox Code Playgroud)

  • 在您需要导入之后,您可能希望执行类似`sys.path.remove(pathYouJustAdded)`的操作,而不是保留此新路径. (4认同)
  • 这可能会在sys.path中添加一个位于Python包内部的目录,从而使相同的模块具有不同的名称以及所有相应的bug。pypy中的autopath.py或twisted中的_preamble.py通过使用搜索条件来解决它,该搜索条件在向上遍历目录时标识顶级包。 (2认同)

Sep*_*ero 31

从目录中导入模块,该目录与当前目录完全相同:

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

  • 我得到:尝试相对导入超出顶级包:( (27认同)
  • 使用“从..导入模块”时出现错误_ValueError:通过遵循建议,尝试在非package_中进行相对导入 (17认同)
  • ImportError: 尝试在没有已知父包的情况下进行相对导入 出现此错误 (2认同)

Tho*_*ner 6

如何加载作为目录的模块

前言:我对之前的答案进行了大量重写,希望能帮助人们轻松进入 Python 的生态系统,并希望通过 Python 的导入系统为每个人带来成功的最佳改变。

这将涵盖package 中的相对导入,我认为这是 OP 问题最可能的情况。

Python是一个模块化系统

这就是为什么我们写import foo从根命名空间加载模块“foo”,而不是写:

foo = dict();  # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh:  # please avoid doing this
    exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo)  # please avoid doing this
Run Code Online (Sandbox Code Playgroud)

Python 没有耦合到文件系统

这就是为什么我们可以在没有实际文件系统的环境中嵌入 python 而不提供虚拟文件系统,例如 Jython。

与文件系统解耦让导入变得灵活,这种设计允许从存档/zip 文件导入、导入单例、字节码缓存、cffi 扩展,甚至远程代码定义加载。

因此,如果导入没有与文件系统耦合,那么“一个目录向上”是什么意思?我们必须选择一些启发式方法,但我们可以这样做,例如,在包中工作时,已经定义了一些启发式方法,使相对导入类似.foo..foo在同一个包中工作。凉爽的!

如果您真诚地想将源代码加载模式与文件系统相结合,您可以这样做。您必须选择自己的启发式方法,并使用某种导入机制,我推荐importlib

Python 的 importlib 示例如下所示:

import importlib.util
import sys

# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'

foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)

foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton
Run Code Online (Sandbox Code Playgroud)

包装

这里有一个很棒的示例项目:https : //github.com/pypa/sampleproject

python 包是关于你的源代码的信息集合,它可以通知其他工具如何将你的源代码复制到其他计算机,以及如何将你的源代码集成到该系统的路径中,以便import foo适用于其他计算机(无论解释器、主机操作系统等)

目录结构

让我们foo在某个目录(最好是一个空目录)中有一个包名。

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here
Run Code Online (Sandbox Code Playgroud)

我的偏好是创建setup.py为 的兄弟foo.py,因为它使编写 setup.py 文件更简单,但是如果您愿意,您可以编写配置来更改/重定向 setuptools 默认所做的一切;例如,放在foo.py“src/”目录下就比较流行,这里就不介绍了。

some_directory/
    foo.py
    setup.py
Run Code Online (Sandbox Code Playgroud)

.

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here
Run Code Online (Sandbox Code Playgroud)

.

some_directory/
    foo.py
    setup.py
Run Code Online (Sandbox Code Playgroud)

“可编辑”又名-e将再次重定向导入机器以加载此目录中的源文件,而不是将当前的确切文件复制到安装环境的库中。这也可能导致开发人员机器上的行为差异,请务必测试您的代码!除了 pip 之外还有其他工具,但是我建议 pip 作为介绍性工具:)

我也喜欢制作foo一个“包”(一个包含 的目录__init__.py)而不是一个模块(一个单一的“.py”文件),“包”和“模块”都可以加载到根命名空间中,模块允许嵌套命名空间,如果我们想要“相对的一个目录”导入,这会很有帮助。

some_directory/
    foo/
        __init__.py
    setup.py
Run Code Online (Sandbox Code Playgroud)

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    py_modules=['foo'],
)
Run Code Online (Sandbox Code Playgroud)

我也喜欢制作一个foo/__main__.py,这允许 python 将包作为模块python3 -m foo执行,例如将foo/__main__.py作为__main__.

some_directory/
    foo/
        __init__.py
        __main__.py  # `if __name__ == "__main__":`  lives here, `def main():` too!
    setup.py
Run Code Online (Sandbox Code Playgroud)

.

python3 -m pip install --editable ./  # or path/to/some_directory/
Run Code Online (Sandbox Code Playgroud)

让我们用更多的模块充实它:基本上,你可以有一个这样的目录结构:

some_directory/
    bar.py           # `import bar`
    foo/
        __init__.py  # `import foo`
        __main__.py
        baz.py       # `import foo.baz
        spam/           
            __init__.py  # `import foo.spam`
            eggs.py      # `import foo.spam.eggs`
    setup.py
Run Code Online (Sandbox Code Playgroud)

setup.py 通常在其中保存有关源代码的元数据信息,例如:

  • 安装名为“install_requires”需要哪些依赖项
  • 包管理应该使用什么名称(安装/卸载“名称”),我建议在我们的例子中这与您的主要 python 包名称匹配foo,尽管用下划线代替连字符很流行
  • 许可信息
  • 成熟度标签(alpha/beta/等),
  • 受众标签(用于开发人员、机器学习等),
  • 单页文档内容(如自述文件),
  • shell 名称(您在用户 shell 中键入的名称,如 bash,或您在图形用户 shell 中找到的名称,如开始菜单),
  • 这个包将安装(和卸载)的python模块列表
  • 事实上的“运行测试”入口点 python ./setup.py test

它非常广泛,如果源模块安装在开发机器上,它甚至可以即时编译 c 扩展。对于日常示例,我推荐PYPA Sample Repository 的 setup.py

如果您要发布构建工件,例如旨在运行几乎相同的计算机的代码副本,则requirements.txt 文件是一种对准确依赖信息进行快照的流行方法,其中“install_requires”是捕获最小和最大兼容版本。但是,鉴于目标机器无论如何几乎相同,我强烈建议创建一个包含整个 python 前缀的 tarball。这可能很棘手,太详细了,无法进入这里。查看pip install--target选项,或 virtualenv aka venv 获取线索。

回到例子

如何将文件导入一个目录:

从 foo/spam/eggs.py,如果我们想要来自 foo/baz 的代码,我们可以通过其绝对命名空间来请求它:

some_directory/
    foo/
        __init__.py
    setup.py
Run Code Online (Sandbox Code Playgroud)

如果我们想保留在将来使用其他相关baz实现将egg.py 移动到其他目录的能力,我们可以使用相对导入,例如:

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
)
Run Code Online (Sandbox Code Playgroud)


JDG*_*JDG 6

为了清楚起见,以下是ThorSummoner答案的三步式、略显简约的版本。它并没有完全达到我想要的效果(我将在底部解释),但它可以正常工作。

第1步:创建目录并setup.py

filepath_to/project_name/
    setup.py
Run Code Online (Sandbox Code Playgroud)

在 中setup.py,写入:

import setuptools

setuptools.setup(name='project_name')
Run Code Online (Sandbox Code Playgroud)

步骤 2:将此目录安装为包

在控制台中运行此代码:

python -m pip install --editable filepath_to/project_name
Run Code Online (Sandbox Code Playgroud)

python您可能需要使用或其他东西来代替python3,具体取决于您的 python 的安装方式。另外,您可以使用-e代替--editable.

现在,您的目录将或多或少看起来像这样。不知道鸡蛋是什么东西

filepath_to/project_name/
    setup.py
    test_3.egg-info/
        dependency_links.txt
        PKG-INFO
        SOURCES.txt
        top_level.txt
Run Code Online (Sandbox Code Playgroud)

该文件夹被视为 python 包,即使您在计算机上的其他位置编写脚本,也可以从此父目录中的文件导入。

步骤 3. 从上面导入

假设您创建了两个文件,一个位于项目的主目录中,另一个位于子目录中。它看起来像这样:

filepath_to/project_name/
    top_level_file.py
    subdirectory/
        subfile.py

    setup.py          |
    test_3.egg-info/  |----- Ignore these guys
        ...           |
Run Code Online (Sandbox Code Playgroud)

现在,如果top_level_file.py看起来像这样:

x = 1
Run Code Online (Sandbox Code Playgroud)

subfile.py然后我可以从计算机上的任何其他文件导入它。

# subfile.py  OR  some_other_python_file_somewhere_else.py

import random # This is a standard package that can be imported anywhere.
import top_level_file # Now, top_level_file.py works similarly.

print(top_level_file.x)
Run Code Online (Sandbox Code Playgroud)

这与我正在寻找的不同:我希望 python 有一种从上面的文件导入的单行方法。相反,我必须将脚本视为模块,执行一堆样板文件,然后全局安装它,以便整个 python 安装能够访问它。这太过分了。如果有人有比不涉及上述过程或importlib恶作剧更简单的方法,请告诉我。