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

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


从…import subpkg2

根据Python文档:当在包层次结构中,使用两个点,就像import语句doc说的那样:

When specifying what module to import you do not have to specify the absolute name of the module. When a module or package is contained within another package it is possible to make a relative import within the same top package without having to mention the package name. By using leading dots in the specified module or package after from you can specify how high to traverse up the current package hierarchy without specifying exact names. One leading dot means the current package where the module making the import exists. Two dots means up one package level. Three dots is up two levels, etc. So if you execute from . import mod from a module in the pkg package then you will end up importing pkg.mod. If you execute from ..subpkg2 import mod from within pkg.subpkg1 you will import pkg.subpkg2.mod. The specification for relative imports is contained within PEP 328.

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


@gimel's answer is correct if you can guarantee the package hierarchy he mentions. If you can't -- if your real need is as you expressed it, exclusively tied to directories and without any necessary relationship to packaging -- then you need to work on __file__ to find out the parent directory (a couple of os.path.dirname calls will do;-), then (if that directory is not already on sys.path) prepend temporarily insert said dir at the very start of sys.path, __import__, remove said dir again -- messy work indeed, but, "when you must, you must" (and Pyhon strives to never stop the programmer from doing what must be done -- just like the ISO C standard says in the "Spirit of C" section in its preface!-).

下面是一个可能对你有用的例子:

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

import sys
sys.path.append("..") # Adds higher directory to python modules path.

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

from .. import module

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.py和foo/__init__.py,我的简单例子:

setup . 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,你可以在编辑模式下将_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))

如何加载一个模块,这是一个目录

前言:我对之前的回答做了大量的重写,希望能帮助人们轻松地进入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没有与文件系统耦合

这就是为什么我们可以在没有实际文件系统的环境中嵌入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

包装

这里有一个很好的项目示例: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/

"editable"即-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 foo将以__main__.py执行foo/__main__.py。

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

py通常保存关于源代码的元数据信息,例如:

what dependencies are needed to install named "install_requires" what name should be used for package management (install/uninstall "name"), I suggest this match your primary python package name in our case foo, though substituting underscores for hyphens is popular licensing information maturity tags (alpha/beta/etc), audience tags (for developers, for machine learning, etc), single-page documentation content (like a README), shell names (names you type at user shell like bash, or names you find in a graphical user shell like a start menu), a list of python modules this package will install (and uninstall) a defacto "run tests" entry point python ./setup.py test

它的扩展性非常强,如果源模块安装在开发机器上,它甚至可以动态编译c扩展。对于一个日常示例,我推荐PYPA示例存储库的setup.py

If you are releasing a build artifact, eg a copy of the code that is meant to run nearly identical computers, a requirements.txt file is a popular way to snapshot exact dependency information, where "install_requires" is a good way to capture minimum and maximum compatible versions. However, given that the target machines are nearly identical anyway, I highly recommend creating a tarball of an entire python prefix. This can be tricky, too detailed to get into here. Check out pip install's --target option, or virtualenv aka venv for leads.

回到这个例子

如何在一个目录上导入文件:

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

import foo.baz

如果我们想保留将来使用其他相对baz实现将eggs.py移动到其他目录的能力,我们可以使用类似这样的相对导入:

import ..baz

为了清晰起见,下面是ThorSummoner的回答的一个三步,有点简单的版本。它不完全是我想要的(我将在底部解释),但它工作正常。

步骤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

而不是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

这个文件夹被认为是一个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安装都能访问它。它是多余的。如果有人有一个更简单的方法,而不涉及上述过程或重要的恶作剧,请告诉我。


@alex-martelli用pathlib优化的答案:

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))

运行python /myprogram/submodule/mymodule.py,导入/myprogram/mainmodule.py,例如via

from mainmodule import *

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

export PYTHONPATH=/myprogram

现在是2022年,没有一个答案真的对我有用。下面是最后奏效的方法

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

我的目录结构:

src ——my_class.py 笔记本电脑 ——mynotebook.ipynb

我从mynotebook.ipynb导入my_class。


您可以使用sys.path.append()方法将包含包的目录添加到搜索模块的路径列表中。例如,如果包位于当前目录以上两个目录,您可以使用以下代码:

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

如果包位于当前目录上方的一个目录,你可以使用下面的代码:

import sys
sys.path.append("..")