厌倦了sys.path的hack方法吗?
虽然有很多sys.path.append
的hack方法可用,但我发现了一种解决手边问题的替代方法。
简介
- 将代码放入一个文件夹中(例如:
packaged_stuff
)
- 创建
setup.py
脚本,并在其中使用setuptools.setup()函数。(参见下面最小化的setup.py
示例)
- 以可编辑状态使用
pip install -e <myproject_folder>
命令来安装该包
- 使用
from packaged_stuff.modulename import function_name
进行导入
设置
起点是您提供的文件结构,放在一个名为myproject
的文件夹中。
.
└── 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
我将把.
称为根文件夹,在我的示例中,它位于C:\tmp\test_imports\
。
api.py
作为测试用例,让我们使用以下路径:./api/api.py
def function_from_api():
return 'I am the return value from api.api!'
test_one.py
from api.api import function_from_api
def test_function():
print(function_from_api())
if __name__ == '__main__':
test_function()
尝试运行 test_one:
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'
试图使用相对导入也行不通:
使用 from ..api.api import function_from_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
步骤
- 在根目录下创建一个
setup.py
文件
setup.py
的内容如下*
from setuptools import setup, find_packages
setup(name='myproject', version='1.0', packages=find_packages())
- 使用虚拟环境
如果您熟悉虚拟环境,请激活一个并跳过下一步。 虽然不是绝对必需的,但在长期运行中(当您有多个进行中的项目时),它们将真正帮助您。最基本的步骤是(在根文件夹中运行)
- 创建虚拟环境
- 激活虚拟环境
source ./venv/bin/activate
(Linux、macOS)或 ./venv/Scripts/activate
(Windows)
要了解更多信息,只需谷歌“python 虚拟环境 教程”或类似内容。您可能永远不需要其他命令,除了创建、激活和停用。
一旦您创建并激活了虚拟环境,控制台应该会在括号中显示虚拟环境的名称。
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]
- 使用
pip install
命令安装你的项目,并加上-e
选项,这样安装后的包可以编辑。任何对于.py文件的修改都会自动地包含在安装的包中。
使用pip
命令安装顶层包myproject
时,需要添加-e
选项,这样它将以可编辑状态安装,所有对.py文件所做的更改都将自动包含在已安装的包中。
在根目录下运行
pip install -e .
(注意点号,它表示“当前目录”)
您还可以使用pip freeze
命令查看已安装的包。
(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
- 将
myproject.
添加到您的导入中
请注意,您只需要将myproject.
添加到那些否则无法工作的导入中。那些没有使用setup.py
和pip install
的导入仍然可以正常工作。请参见下面的示例。
测试解决方案
现在,让我们使用上面定义的api.py
和下面定义的test_one.py
来测试解决方案。
test_one.py
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!
* 查看setuptools文档获取更详细的setup.py示例。
** 实际上,您可以将虚拟环境放在硬盘上的任何位置。
sys.path
黑科技,阅读到目前为止唯一实际的解决方案(在7年之后!)链接。 - Aran-Fey