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

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


当前回答

@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

其他回答

为了清晰起见,下面是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安装都能访问它。它是多余的。如果有人有一个更简单的方法,而不涉及上述过程或重要的恶作剧,请告诉我。

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

前言:我对之前的回答做了大量的重写,希望能帮助人们轻松地进入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
import sys
sys.path.append("..") # Adds higher directory to python modules path.

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

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

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

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

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