如何为基本包设置配置 __main__.py、__init__.py 和 setup.py?
How to configure __main__.py, __init__.py, and setup.py for a basic package setup?
背景:
我的目录结构如下:
Package/
setup.py
src/
__init__.py
__main__.py
code.py
我希望能够 运行 以多种不同的方式编写代码。
pip install Package
然后python
然后from Package import *
python -m Package
应该做 __main__.py
中的事情
python __main__.py
也应该做 __main__.py
中的事情,但这次,我们假设您下载的是源代码而不是 pip installing
。
现在我已经开始使用前两个了,但是设置很乱:
setup.py:
setup(
name='Package',
packages=['Package'],
package_dir={'Package': 'src'},
...
entry_points={ 'console_scripts': ['Package = src.__main__:main' ] }
__init__.py:
from Package.code import .......
__main__.py:
from . import .......
对我来说更有意义的是在这两种情况下写
from code import ........
但这给了我导入错误。
问题:
我的方法真的是唯一的方法吗?
最重要的是,我如何支持第三个用例?现在,python __main__.py
抛出
File "__main__.py", line 10, in <module>
from . import code
ImportError: cannot import name 'class defined in code.py'
备注:
我已阅读
from code import .........
失败,因为您的系统上没有安装名为 code
的 Python 软件包。在您的系统上有一个名为 code
的 Python 模块 ,但是在您的导入语句中您没有指定您的 code
模块可以使用的包在
您在 src/
中的 __init__.py
文件的目的告诉 Python src/
目录应该被视为一个 Python 包,其中其内容作为包内的模块。由于 code.py
与您的 __init__.py
文件一起位于 src/
中,因此您的 code
模块位于您的 src
包中。
现在您知道可以在哪个包中找到您的 code
模块,您可以从中导入内容:
from src.code import .........
此外,作为旁注:__init__.py
仅通过出现在您的 src/
目录中来完成它的工作,因此它甚至不需要包含任何代码。因此,将 __init__.py
文件留空通常是个好主意。
我经常使用此设置,因为它与 python setup.py develop
配合使用效果更好
Package_root/
setup.py
src/
Package/
__init__.py
__main__.py
code.py
这可能不是(还)是您期望的详细答案,但我认为这三个用例值得一试。
setup( ...
package_dir = {'': 'src'},
entry_points = {'console_scripts': ['Package = Package.__main__:main'],},
packages = find_packages(exclude=["Package.egg_info",]),
...)
您几乎拥有所需的一切(甚至更多)!我会采用以下设置:
code.py:
foo = 1
__init__.py:
from .code import foo
这里做相对导入,因为导入整个包时会用到__init__.py
。请注意,我们使用 .
语法将导入明确标记为相对导入,因为这是 Python 3 所必需的(如果您使用了 from __future__ import absolute_import
,则在 Python 2 中是必需的)。 =74=]
__main__.py:
from Package import foo
print('foo = ', foo)
这是包的主要脚本,因此我们使用绝对 import
语句。通过这样做,我们假设包已经安装(或者至少已经放在路径上);这就是处理包裹的方式!您可能认为这与您的第三个用例冲突,但实际上在处理包时没有理由 not 到 pip install
。这真的没什么大不了的(尤其是在使用 virtualenv
时)!
如果您关心的是修改源文件并通过 运行 宁 __main__.py
文件轻松观察更改,那么您可以简单地使用 -e
("editable") 切换:pip install -e .
(假设您在目录 Package
中)。但是,对于您当前的目录结构,这将不起作用,因为 -e
开关会将 egg-link
放置到包含 setup.py
文件的目录;此目录不包含名为 Package
的包,而是包含 src
(我有 a question about that)。
相反,如果您按照惯例在包本身之后命名包源的根目录(对于您的示例是 Package
),那么使用 -e
安装不是问题: Python是否在对应目录下找到需要的包Package
:
$ tree Package/
Package/
├── setup.py
└── Package <-- Renamed "src" to "Package" because that's the package's name.
├── code.py
├── __init__.py
└── __main__.py
这也让您可以省略 setup.py
中 package_dir={'Package': 'src'}
的额外定义。
关于 setup.py
的说明:对于您指定的三个用例,无需定义入口点。那就是你可以跳过行entry_points={ 'console_scripts': ['Package = src.__main__:main' ] }
。通过发布 __main__.py
模块,python -m Package
将很容易地执行该模块中的代码。您还可以添加一个额外的 if 子句:
def main():
print('foo = ', foo)
if __name__ == '__main__':
main()
另一方面,入口点让您可以直接从 CLI 执行 __main__.main
中的代码;即运行ning $ Package
会执行相应的代码
回顾
底线是我在处理包时总是使用 pip install
。为什么不呢,特别是如果您已经创建了 setup.py
文件?如果要应用对包的更改 "in real-time",那么您可以使用 -e
开关进行安装(这可能需要重命名 src
文件夹,见上文)。因此,您的第三个用例将读作 "Download the source and pip install (-e) Package
(within a virtualenv); then you can run python __main__.py
".
编辑
运行 __main__.py
无 pip install
如果你不想通过 pip 安装包但仍然可以 运行 __main__.py
脚本,我仍然会使用上面的设置。然后我们需要确保 from Package import ...
语句仍然成功,这可以通过扩展导入路径来实现(请注意,这需要将 src
目录重命名为包的名称!).
修改PYTHONPATH
对于Linux bash你可以设置Python路径如下:
export PYTHONPATH=$PYTHONPATH:/path/to/Package
或者如果您与 __main__.py
在同一目录中:
export PYTHONPATH=$PYTHONPATH:`cd ..; pwd`
当然不同的操作系统有不同的方法。
扩展__main__.py
中的路径
您(或者您的同事)可以将以下行添加到脚本的顶部(在 from Package import ...
语句之前):
import sys
sys.path.append('/path/to/Package')
扩展sitecustomize.py
中的路径
您可以将名为 sitecustomize.py
的模块放置在 Python 安装的 lib/python3.5/site-packages/
目录中,其中包含以下行:
import sys
sys.path.append('/path/to/Package')
创建一个单独的顶级 main.py
脚本
所以你会有以下布局:
$ tree Package/
Package/
├── main.py <-- Add this file.
├── setup.py
└── src
├── code.py
├── __init__.py
└── __main__.py
其中 main.py
包含
import src.__main__
现在 __main__.py
被视为 src
包的一部分,相关导入将起作用。
而不是 运行ning python src/__main__.py
你现在 运行 python main.py
。
背景:
我的目录结构如下:
Package/
setup.py
src/
__init__.py
__main__.py
code.py
我希望能够 运行 以多种不同的方式编写代码。
pip install Package
然后python
然后from Package import *
python -m Package
应该做__main__.py
中的事情
python __main__.py
也应该做__main__.py
中的事情,但这次,我们假设您下载的是源代码而不是pip installing
。
现在我已经开始使用前两个了,但是设置很乱:
setup.py:
setup(
name='Package',
packages=['Package'],
package_dir={'Package': 'src'},
...
entry_points={ 'console_scripts': ['Package = src.__main__:main' ] }
__init__.py:
from Package.code import .......
__main__.py:
from . import .......
对我来说更有意义的是在这两种情况下写
from code import ........
但这给了我导入错误。
问题:
我的方法真的是唯一的方法吗?
最重要的是,我如何支持第三个用例?现在,python __main__.py
抛出
File "__main__.py", line 10, in <module>
from . import code
ImportError: cannot import name 'class defined in code.py'
备注:
我已阅读
from code import .........
失败,因为您的系统上没有安装名为 code
的 Python 软件包。在您的系统上有一个名为 code
的 Python 模块 ,但是在您的导入语句中您没有指定您的 code
模块可以使用的包在
您在 src/
中的 __init__.py
文件的目的告诉 Python src/
目录应该被视为一个 Python 包,其中其内容作为包内的模块。由于 code.py
与您的 __init__.py
文件一起位于 src/
中,因此您的 code
模块位于您的 src
包中。
现在您知道可以在哪个包中找到您的 code
模块,您可以从中导入内容:
from src.code import .........
此外,作为旁注:__init__.py
仅通过出现在您的 src/
目录中来完成它的工作,因此它甚至不需要包含任何代码。因此,将 __init__.py
文件留空通常是个好主意。
我经常使用此设置,因为它与 python setup.py develop
Package_root/
setup.py
src/
Package/
__init__.py
__main__.py
code.py
这可能不是(还)是您期望的详细答案,但我认为这三个用例值得一试。
setup( ...
package_dir = {'': 'src'},
entry_points = {'console_scripts': ['Package = Package.__main__:main'],},
packages = find_packages(exclude=["Package.egg_info",]),
...)
您几乎拥有所需的一切(甚至更多)!我会采用以下设置:
code.py:
foo = 1
__init__.py:
from .code import foo
这里做相对导入,因为导入整个包时会用到__init__.py
。请注意,我们使用 .
语法将导入明确标记为相对导入,因为这是 Python 3 所必需的(如果您使用了 from __future__ import absolute_import
,则在 Python 2 中是必需的)。 =74=]
__main__.py:
from Package import foo
print('foo = ', foo)
这是包的主要脚本,因此我们使用绝对 import
语句。通过这样做,我们假设包已经安装(或者至少已经放在路径上);这就是处理包裹的方式!您可能认为这与您的第三个用例冲突,但实际上在处理包时没有理由 not 到 pip install
。这真的没什么大不了的(尤其是在使用 virtualenv
时)!
如果您关心的是修改源文件并通过 运行 宁 __main__.py
文件轻松观察更改,那么您可以简单地使用 -e
("editable") 切换:pip install -e .
(假设您在目录 Package
中)。但是,对于您当前的目录结构,这将不起作用,因为 -e
开关会将 egg-link
放置到包含 setup.py
文件的目录;此目录不包含名为 Package
的包,而是包含 src
(我有 a question about that)。
相反,如果您按照惯例在包本身之后命名包源的根目录(对于您的示例是 Package
),那么使用 -e
安装不是问题: Python是否在对应目录下找到需要的包Package
:
$ tree Package/
Package/
├── setup.py
└── Package <-- Renamed "src" to "Package" because that's the package's name.
├── code.py
├── __init__.py
└── __main__.py
这也让您可以省略 setup.py
中 package_dir={'Package': 'src'}
的额外定义。
关于 setup.py
的说明:对于您指定的三个用例,无需定义入口点。那就是你可以跳过行entry_points={ 'console_scripts': ['Package = src.__main__:main' ] }
。通过发布 __main__.py
模块,python -m Package
将很容易地执行该模块中的代码。您还可以添加一个额外的 if 子句:
def main():
print('foo = ', foo)
if __name__ == '__main__':
main()
另一方面,入口点让您可以直接从 CLI 执行 __main__.main
中的代码;即运行ning $ Package
会执行相应的代码
回顾
底线是我在处理包时总是使用 pip install
。为什么不呢,特别是如果您已经创建了 setup.py
文件?如果要应用对包的更改 "in real-time",那么您可以使用 -e
开关进行安装(这可能需要重命名 src
文件夹,见上文)。因此,您的第三个用例将读作 "Download the source and pip install (-e) Package
(within a virtualenv); then you can run python __main__.py
".
编辑
运行 __main__.py
无 pip install
如果你不想通过 pip 安装包但仍然可以 运行 __main__.py
脚本,我仍然会使用上面的设置。然后我们需要确保 from Package import ...
语句仍然成功,这可以通过扩展导入路径来实现(请注意,这需要将 src
目录重命名为包的名称!).
修改PYTHONPATH
对于Linux bash你可以设置Python路径如下:
export PYTHONPATH=$PYTHONPATH:/path/to/Package
或者如果您与 __main__.py
在同一目录中:
export PYTHONPATH=$PYTHONPATH:`cd ..; pwd`
当然不同的操作系统有不同的方法。
扩展__main__.py
中的路径
您(或者您的同事)可以将以下行添加到脚本的顶部(在 from Package import ...
语句之前):
import sys
sys.path.append('/path/to/Package')
扩展sitecustomize.py
中的路径
您可以将名为 sitecustomize.py
的模块放置在 Python 安装的 lib/python3.5/site-packages/
目录中,其中包含以下行:
import sys
sys.path.append('/path/to/Package')
创建一个单独的顶级 main.py
脚本
所以你会有以下布局:
$ tree Package/
Package/
├── main.py <-- Add this file.
├── setup.py
└── src
├── code.py
├── __init__.py
└── __main__.py
其中 main.py
包含
import src.__main__
现在 __main__.py
被视为 src
包的一部分,相关导入将起作用。
而不是 运行ning python src/__main__.py
你现在 运行 python main.py
。