我想从位于当前目录上方的目录中的文件中的类继承。
是否可以相对导入该文件?
from ..subpkg2 import mod
根据 Python 文档:在包层次结构中,使用两个点,正如import 语句文档所说:
在指定要导入的模块时,您不必指定模块的绝对名称。当模块或包包含在另一个包中时,可以在同一个顶级包中进行相对导入,而无需提及包名。通过在指定的模块或包中使用前导点,
from
您可以指定向上遍历当前包层次结构的高度,而无需指定确切的名称。一个前导点表示进行导入的模块所在的当前包。两个点表示上一层封装。三个点是两个级别,等等。因此,如果您from . import mod
从包中的模块执行,pkg
那么您最终将导入pkg.mod
. 如果您from ..subpkg2 import mod
从内部执行,pkg.subpkg1
您将导入pkg.subpkg2.mod
. 相对导入的规范包含在PEP 328中。
PEP 328处理绝对/相对导入。
import sys
sys.path.append("..") # Adds higher directory to python modules path.
如果您可以保证他提到的包层次结构,@gimel 的答案是正确的。如果你不能——如果你真正的需要是你所表达的,完全依赖于目录并且与打包没有任何必要的关系——那么你需要努力__file__
找出父目录(几个os.path.dirname
电话就可以了; -),然后(如果该目录尚未打开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
从恰好比当前目录高一级的目录导入模块:
from .. import module
前言:我对之前的答案进行了大量重写,希望能帮助人们轻松进入 python 的生态系统,并希望通过 python 的导入系统给每个人带来成功的最佳改变。
这将涵盖package 中的相对导入,我认为这是 OP 问题最可能的情况。
这就是为什么我们写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
这就是为什么我们可以在不提供虚拟文件系统(例如 Jython)的情况下将 python 嵌入到没有事实上的文件系统的环境中。
与文件系统分离后,导入变得灵活,这种设计允许从归档/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/
“可编辑”又名-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
执行,例如将foo/__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
通常包含有关源代码的元数据信息,例如:
foo
,尽管用下划线代替连字符很流行python ./setup.py test
它非常广泛,如果源模块安装在开发机器上,它甚至可以即时编译 c 扩展。对于日常示例,我推荐PYPA 示例存储库的 setup.py
如果您要发布构建工件,例如旨在运行几乎相同计算机的代码副本,则 requirements.txt 文件是快照精确依赖关系信息的常用方法,其中“install_requires”是捕获最小和最大兼容版本。但是,鉴于目标机器几乎相同,我强烈建议创建一个包含整个 python 前缀的 tarball。这可能很棘手,太详细而无法进入这里。查看pip install
's--target
选项,或 virtualenv aka venv 以获得潜在客户。
回到例子
从 foo/spam/eggs.py 中,如果我们想要来自 foo/baz 的代码,我们可以通过它的绝对命名空间来请求它:
import foo.baz
如果我们想保留将来通过其他相关实现将 egg.py 移动到其他目录的能力baz
,我们可以使用相对导入,例如:
import ..baz
为了清楚起见,这是ThorSummoner答案的三步,有点简约的版本。它并不完全符合我的要求(我将在底部解释),但它可以正常工作。
setup.py
filepath_to/project_name/
setup.py
在setup.py
中,写:
import setuptools
setuptools.setup(name='project_name')
在控制台中运行此代码:
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 包,即使你在计算机上的其他任何地方编写脚本,你也可以从这个父目录中的文件导入。
假设您制作了两个文件,一个在项目的主目录中,另一个在子目录中。它看起来像这样:
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
恶作剧更简单的方法,请告诉我。
@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
which imports /myprogram/mainmodule.py
,例如,通过
from mainmodule import *
在 Linux 上(例如,在python Docker 映像中),我必须将程序根目录添加到PYTHONPATH
:
export PYTHONPATH=/myprogram
为了可靠地加载 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.
.
请务必声明您的setuptools.setup()
in setup.py
,
官方示例:https ://github.com/pypa/sampleproject/blob/master/setup.py
在我们的例子中,我们可能想要导出bar.py
and foo/__init__.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 和包
#!/usr/bin/env python3
import bar
import foo
print(dir(bar))
print(dir(foo))