ChatGPT解决这个技术问题 Extra ChatGPT

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

我想从位于当前目录上方的目录中的文件中的类继承。

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


f
fIwJlxSzApHEZIl

from ..subpkg2 import mod

根据 Python 文档:在包层次结构中,使用两个点,如 import statement 文档所述:

在指定要导入的模块时,您不必指定模块的绝对名称。当模块或包包含在另一个包中时,可以在同一个顶级包中进行相对导入,而无需提及包名。通过在指定模块或包中使用前导点 from 您可以指定向上遍历当前包层次结构的高度,而无需指定确切的名称。一个前导点表示进行导入的模块所在的当前包。两个点表示向上一个封装级别。三个点向上两个级别,等等。所以如果你从 .从 pkg 包中的模块导入 mod 然后您将最终导入 pkg.mod。如果您从 pkg.subpkg1 中执行 from ..subpkg2 import mod,您将导入 pkg.subpkg2.mod。相对进口的规范包含在 PEP 328 中。

PEP 328 处理绝对/相对导入。


up1 = os.path.abspath('..') sys.path.insert(0, up1)
Pep 328 仅显示 Python 版本:2.4、2,5、2.6。第 3 版留给知识渊博的人。
这会触发错误 ValueError:尝试相对导入超出顶级包
导致 ImportError:没有已知父包的尝试相对导入
S
Sepero
import sys
sys.path.append("..") # Adds higher directory to python modules path.

这对我有用。添加后,我可以直接导入父模块,无需使用“..”。
粗略地说,只有当应用程序的 PWD 值(其当前的directeroy)是父级的子级时才有效。因此,即使它有效,多种系统性变化也会将其赶走。
这对我来说也适用于导入更高级别的模块。我将它与 os.chdir("..") 一起使用来加载更高级别的其他文件。
这似乎触发了与上述来自 gimel 的答案相同的错误。
J
Jacob Marble

@gimel 的回答是正确的 if 你可以保证他提到的包层次结构。如果您不能 - 如果您的真正需求如您所表达的那样,仅与目录相关且与打包没有任何必要关系 - 那么您需要在 __file__ 上工作以找出父目录(几个 { 2} 调用就可以了;-),然后(如果该目录尚未在 sys.path 上)在 sys.path__import__ 的开头临时插入所述目录,再次删除所述目录 - 确实是混乱的工作,但是,“当你必须,你必须”(并且 Pyhon 力求从不阻止程序员做 必须 做的事情——就像 ISO C 标准在“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

这可能会向 sys.path 添加一个 inside Python 包的目录,从而使相同的模块以不同的名称和所有相应的错误可用。 pypy 中的 autopath.py 或twisted 中的 _preamble.py 通过使用在向上遍历目录时识别顶级包的搜索条件来解决它。
您可能希望在需要的导入之后执行 sys.path.remove(pathYouJustAdded) 之类的操作,以免保留此新路径。
P
Praveen

从恰好比当前目录高一级的目录导入模块:

from .. import module

我得到:尝试相对导入超出顶级包:(
使用 from .. import module 我得到错误 ValueError: Attempted relative import in non-package 按照建议
ImportError:尝试在没有已知父包的情况下进行相对导入收到此错误
那是因为您需要将脚本作为模块 @user3428154 和 SayAz 运行。
T
ThorSummoner

如何加载作为目录的模块

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

这将涵盖一个包中的相对导入,我认为这是 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

Python 没有与文件系统耦合

这就是为什么我们可以在不提供虚拟文件系统(例如 Jython)的情况下将 python 嵌入到没有事实上的文件系统的环境中。

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

因此,如果导入不与文件系统耦合,那么“一个目录向上”是什么意思?我们必须选择一些启发式方法,但我们可以这样做,例如在 package 中工作时,已经定义了一些启发式方法,使 relative imports like .foo and ..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

打包

此处官方提供了一个很好的示例项目:https://github.com/pypa/sampleproject

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

目录结构

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

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here

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

some_directory/
    foo.py
    setup.py

.

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

import setuptools

setuptools.setup(
    name="foo",
    ...
    py_modules=['foo'],
)

.

python3 -m pip install --editable ./  # or path/to/some_directory/

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

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

some_directory/
    foo/
        __init__.py
    setup.py

.

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

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
)

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

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

.

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

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
    ...
    entry_points={
        'console_scripts': [
            # "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
            'foo=foo.__main__:main',
        ]
    },
)

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

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

setup.py 通常包含有关源代码的元数据信息,例如:

安装名为“install_requires”的依赖项

包管理应该使用什么名称(安装/卸载“名称”),我建议在我们的例子中匹配你的主要 python 包名称 foo,尽管用下划线代替连字符很流行

许可信息

成熟度标签(alpha/beta/etc),

受众标签(针对开发人员、机器学习等),

单页文档内容(如自述文件),

shell 名称(您在用户 shell 中键入的名称,如 bash,或您在图形用户 shell 中找到的名称,如开始菜单),

这个包将安装(和卸载)的python模块列表

一个事实上的“运行测试”入口点 python ./setup.py 测试

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

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

回到例子

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

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

import foo.baz

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

import ..baz

为了清楚起见,您是说要使用 import foo.baz 还是 import ..baz,我需要在正确制作的 setup.py 文件上运行 python3 -m pip install --editable FILEPATH?令人惊讶的是,python 需要如此多的开销才能从 above 进行导入。 . .
我做了所有这些,现在我可以从计算机上任何地方的任何 python 脚本导入 foo。有没有什么方法可以从上面的文件中导入而不需要 importlib 和所有样板?
J
JDG

为清楚起见,这里是 ThorSummoner's 答案的三步,有点简约的版本。它并不完全符合我的要求(我将在底部解释),但它可以正常工作。

第 1 步:制作目录和 setup.py

filepath_to/project_name/
    setup.py

setup.py 中,写入:

import setuptools

setuptools.setup(name='project_name')

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

在控制台中运行此代码:

python -m pip install --editable filepath_to/project_name

您可能需要使用 python3 或其他东西,而不是 python,这取决于您的 Python 的安装方式。此外,您可以使用 -e 代替 --editable

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

filepath_to/project_name/
    setup.py
    test_3.egg-info/
        dependency_links.txt
        PKG-INFO
        SOURCES.txt
        top_level.txt

这个文件夹被认为是一个 python 包,即使你在计算机上的其他任何地方编写脚本,你也可以从这个父目录中的文件导入。

步骤 3. 从上面导入

假设您制作了两个文件,一个在项目的主目录中,另一个在子目录中。它看起来像这样:

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

    setup.py          |
    test_3.egg-info/  |----- Ignore these guys
        ...           |

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

x = 1

然后我可以从 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)

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


您始终可以在虚拟环境中执行此操作,而不是使用 env\Scripts\python ...env/bin/python 全局安装(您的项目正在使用的虚拟环境)
S
SzieberthAdam

@alex-martellipathlib 的优化答案:

import pathlib
import sys

_parentdir = pathlib.Path(__file__).parent.parent.resolve()
sys.path.insert(0, str(_parentdir))

import module_in_parent_dir

sys.path.remove(str(_parentdir))

F
F1iX

运行导入 /myprogram/mainmodule.pypython /myprogram/submodule/mymodule.py,例如,通过

from mainmodule import *

在 Linux 上(例如,在 python Docker image 中),我必须将程序根目录添加到 PYTHONPATH

export PYTHONPATH=/myprogram

T
ThorSummoner

Python是一个模块化系统

Python 不依赖文件系统

为了可靠地加载 python 代码,将该代码放在一个模块中,并将该模块安装在 python 的库中。

始终可以使用 import <name> 从顶级命名空间加载已安装的模块

这里有一个很好的示例项目正式可用:https://github.com/pypa/sampleproject

基本上,你可以有这样的目录结构:

the_foo_project/
    setup.py  

    bar.py           # `import bar`
    foo/
      __init__.py    # `import foo`

      baz.py         # `import foo.baz`

      faz/           # `import foo.faz`
        __init__.py
        daz.py       # `import foo.faz.daz` ... etc.

.

请务必在 setup.py 中声明您的 setuptools.setup()

官方示例:https://github.com/pypa/sampleproject/blob/master/setup.py

在我们的例子中,我们可能想要导出 bar.pyfoo/__init__.py,我的简短示例:

安装程序.py

#!/usr/bin/env python3

import setuptools

setuptools.setup(
    ...
    py_modules=['bar'],
    packages=['foo'],
    ...
    entry_points={}, 
        # Note, any changes to your setup.py, like adding to `packages`, or
        # changing `entry_points` will require the module to be reinstalled;
        # `python3 -m pip install --upgrade --editable ./the_foo_project
)

.

现在我们可以将我们的模块安装到python库中;使用 pip,您可以在编辑模式下将 the_foo_project 安装到您的 python 库中,这样我们就可以实时处理它

python3 -m pip install --editable=./the_foo_project

# if you get a permission error, you can always use 
# `pip ... --user` to install in your user python library

.

现在从任何 python 上下文中,我们可以加载我们共享的 py_modules 和包

foo_script.py

#!/usr/bin/env python3

import bar
import foo

print(dir(bar))
print(dir(foo))

应该提到我总是在使用 pip install --edit foo 处理模块时安装我的模块,几乎总是在 virtualenv 中。我几乎从不编写不打算安装的模块。如果我误解了我想知道的事情。
我还应该提到,使用 venv-auto-creation 测试套件,比如 tox 非常有用,因为 editable 鸡蛋链接和安装的 python 模块在各个方面都不完全相同;例如,将在路径查找中找到向可编辑模块添加新命名空间,但如果未在 setup.py 文件中导出,则不会打包/安装!测试你的用例:)