Python 兄弟包导入
我试着通读有关兄弟姐妹导入的问题,甚至是 ,但我还没有找到答案 结构如下:Python 兄弟包导入,python,packages,python-import,siblings,Python,Packages,Python Import,Siblings,我试着通读有关兄弟姐妹导入的问题,甚至是 ,但我还没有找到答案 结构如下: ├── LICENSE.md ├── README.md ├── api │ ├── __init__.py │ ├── api.py │ └── api_key.py ├── examples │ ├── __init__.py │ ├── example_one.py │ └── example_two.py └── tests │ ├── __init__.py │ └── test
├── LICENSE.md
├── README.md
├── api
│ ├── __init__.py
│ ├── api.py
│ └── api_key.py
├── examples
│ ├── __init__.py
│ ├── example_one.py
│ └── example_two.py
└── tests
│ ├── __init__.py
│ └── test_one.py
.
|__Main.py
|
|__Siblings
|
|___sib1
| |
| |__call.py
|
|___sib2
|
|__callsib.py
示例
和测试
目录中的脚本如何从
api
模块,是否可以从命令行运行
另外,我希望避免对每个文件使用丑陋的sys.path.insert
hack。当然
这可以在Python中完成,对吗?您需要查看导入语句是如何在相关代码中编写的。如果
examples/example\u one.py
使用以下导入语句:
import api.api
…然后它希望项目的根目录位于系统路径中
在没有任何黑客攻击的情况下(如您所说),支持此操作的最简单方法是从顶级目录运行示例,如下所示:
PYTHONPATH=$PYTHONPATH:. python examples/example_one.py
您需要查看导入语句是如何在相关代码中编写的。如果
examples/example\u one.py
使用以下导入语句:
import api.api
…然后它希望项目的根目录位于系统路径中
在没有任何黑客攻击的情况下(如您所说),支持此操作的最简单方法是从顶级目录运行示例,如下所示:
PYTHONPATH=$PYTHONPATH:. python examples/example_one.py
首先,您应该避免使用与模块本身同名的文件。它可能破坏其他进口商品 导入文件时,解释器首先检查当前目录,然后搜索全局目录 在
示例
或测试
中,您可以调用:
from ..api import api
首先,您应该避免使用与模块本身同名的文件。它可能破坏其他进口商品 导入文件时,解释器首先检查当前目录,然后搜索全局目录 在
示例
或测试
中,您可以调用:
from ..api import api
七年后
自从我写了下面的答案之后,修改sys.path仍然是一个快速而肮脏的技巧,对于私有脚本来说效果很好,但是已经有了一些改进
- 软件包(无论是否在virtualenv中)将为您提供所需的内容,不过我建议使用pip来实现这一点,而不是直接使用setuptools(并使用
来存储元数据)setup.cfg
- 作为一个包运行也很有效(但是如果你想把你的工作目录转换成一个可安装的包,结果会有点尴尬)
- 对于测试,特别是在这种情况下,能够找到api包,并为您处理
hackssys.path
pip-e
安装可能是您最好的选择,即使它还不完美
旧答案
正如在其他地方已经指出的,可怕的事实是,您必须进行丑陋的黑客攻击,以允许从\uuuuu main\uuuu
模块从兄弟模块或父包导入。有关问题的详细信息,请参阅。试图以更合理的方式处理导入,但Guido拒绝了它
唯一的用例似乎是运行发生的脚本
住在一个模块的目录里,我一直认为这是一个
反模式
()
尽管如此,我还是经常使用这种模式
# Ugly hack to allow absolute import from the root folder
# whatever its name is. Please forgive the heresy.
if __name__ == "__main__" and __package__ is None:
from sys import path
from os.path import dirname as dir
path.append(dir(path[0]))
__package__ = "examples"
import api
这里的path[0]
是您正在运行的脚本的父文件夹,而dir(path[0])
是您的顶级文件夹
不过,我仍然无法使用相对导入,但它确实允许从顶层进行绝对导入(在您的示例api
的父文件夹中)。七年后
自从我写了下面的答案之后,修改sys.path仍然是一个快速而肮脏的技巧,对于私有脚本来说效果很好,但是已经有了一些改进
- 软件包(无论是否在virtualenv中)将为您提供所需的内容,不过我建议使用pip来实现这一点,而不是直接使用setuptools(并使用
来存储元数据)setup.cfg
- 作为一个包运行也很有效(但是如果你想把你的工作目录转换成一个可安装的包,结果会有点尴尬)
- 对于测试,特别是在这种情况下,能够找到api包,并为您处理
hackssys.path
pip-e
安装可能是您最好的选择,即使它还不完美
旧答案
正如在其他地方已经指出的,可怕的事实是,您必须进行丑陋的黑客攻击,以允许从\uuuuu main\uuuu
模块从兄弟模块或父包导入。有关问题的详细信息,请参阅。试图以更合理的方式处理导入,但Guido拒绝了它
唯一的用例似乎是运行发生的脚本
住在一个模块的目录里,我一直认为这是一个
反模式
()
尽管如此,我还是经常使用这种模式
# Ugly hack to allow absolute import from the root folder
# whatever its name is. Please forgive the heresy.
if __name__ == "__main__" and __package__ is None:
from sys import path
from os.path import dirname as dir
path.append(dir(path[0]))
__package__ = "examples"
import api
这里的path[0]
是您正在运行的脚本的父文件夹,而dir(path[0])
是您的顶级文件夹
不过,我仍然无法使用相对导入,但它确实允许从顶层(在您的示例
api
的父文件夹中)进行绝对导入。我在tests
文件夹的Python文件顶部插入了另一个替代方法:
# Path hack.
import sys, os
sys.path.insert(0, os.path.abspath('..'))
下面是我在
tests
文件夹中Python文件顶部插入的另一个备选方案:
# Path hack.
import sys, os
sys.path.insert(0, os.path.abspath('..'))
我还没有必要理解Pythonology,以了解在没有兄弟/相对导入黑客的情况下在无关项目之间共享代码的预期方式。直到那天,这是我的解决方案。对于
示例
或测试
从导入内容。\a
if __name__ == '__main__':
if __package__ is None:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
import api
else:
import ..api.api
.
└── myproject
├── api
│ ├── api_key.py
│ ├── api.py
│ └── __init__.py
├── examples
│ ├── example_one.py
│ ├── example_two.py
│ └── __init__.py
├── LICENCE.md
├── README.md
└── tests
├── __init__.py
└── test_one.py
def function_from_api():
return 'I am the return value from api.api!'
from api.api import function_from_api
def test_function():
print(function_from_api())
if __name__ == '__main__':
test_function()
PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
File ".\myproject\tests\test_one.py", line 1, in <module>
from api.api import function_from_api
ModuleNotFoundError: No module named 'api'
PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
File ".\tests\test_one.py", line 1, in <module>
from ..api.api import function_from_api
ValueError: attempted relative import beyond top-level package
from setuptools import setup, find_packages
setup(name='myproject', version='1.0', packages=find_packages())
PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>
.
├── myproject
│ ├── api
│ │ ├── api_key.py
│ │ ├── api.py
│ │ └── __init__.py
│ ├── examples
│ │ ├── example_one.py
│ │ ├── example_two.py
│ │ └── __init__.py
│ ├── LICENCE.md
│ ├── README.md
│ └── tests
│ ├── __init__.py
│ └── test_one.py
├── setup.py
└── venv
├── Include
├── Lib
├── pyvenv.cfg
└── Scripts [87 entries exceeds filelimit, not opening dir]
(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///C:/tmp/test_imports
Installing collected packages: myproject
Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0
from myproject.api.api import function_from_api
def test_function():
print(function_from_api())
if __name__ == '__main__':
test_function()
(venv) PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
I am the return value from api.api!
.
|__Main.py
|
|__Siblings
|
|___sib1
| |
| |__call.py
|
|___sib2
|
|__callsib.py
import sib1.call as call
def main():
call.Call()
if __name__ == '__main__':
main()
import sib2.callsib as callsib
def Call():
callsib.CallSib()
if __name__ == '__main__':
Call()
def CallSib():
print("Got Called")
if __name__ == '__main__':
CallSib()
setup.py
src/
mypkg/
__init__.py
app.py
view.py
tests/
__init__.py
foo/
__init__.py
test_view.py
bar/
__init__.py
test_view.py
from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())
pip install -e .