如何组织Python软件包

如何组织Python软件包

目录

1 前言2 relative和absolute导入3 覆盖标准库问题4 独立运行脚本或软件包5 文档目录6 其它常见问题7 pip/pip/__main__.py8 后记9 资料

1 前言

就如 stackoverflow 上问题 how-to-do-relative-imports-in-python 所描述,最近也遇到了Python软件包组织结构的烦恼。

查资料的结果是 http://blog.habnab.it/blog/2013/07/21/python-packages-and-you/ 说的比较明白,这就做个简单地笔记.

2 relative和absolute导入

absolute-import,比如:

from P.A import B

relative-import,比如:

from .A import B

应该尽量保持使用软件包绝对路径的方式,一方面能够让代码更加清晰明了,另一方面相对路径方式需要比较高版本的Python才支持。

3 覆盖标准库问题

如果自定义的软件包与标准库中某个软件重名,则在导入的时候,可能导入的不是自己想要的.

比如标准库中有A,而自定义一个软件也叫A,则在A中可以强制绝对路径导入:

from __future__ import absolute_import

这样,在A中导入A的时候,找的是标准库里面的软件包A,如 pip/pip/_vendor/__init__.py:

"""pip._vendor is for vendoring dependencies of pip to prevent needing pip todepend on something external.Files inside of pip._vendor should be considered immutable and should only beupdated to versions from upstream."""from __future__ import absolute_import

在自定义包中的 __init__.py 文件中应该总是加入这行.

4 独立运行脚本或软件包

如果要运行一个软件包A,则应采用这种方式:

$ python -m A

只需要创建 A/__main__.py ,则上面这种方式会自动调用此文件;通过这种方式,可以很方便的运行软件包。

而对于软件包中某个模块B.py,则常常会这样测试:

if __name__ == "__main__":    #....

这种方式的问题,一方面,当 __name__ 等于 __main__ 的时候,包中模块的相对位置关系都没有了,很可能导致报错;另一方面,不好维护。

为了方便维护与测试,可以在C.py中:

def main(args=None):    #...if __name__ == "__main__":    import sys    sys.exit(main())

运行的时候也不要直接(__package__ 是 None):

$ python A/B/C.py

为了保持包中模块的相对位置关系,应该(__package__是A.B):

$ python -m A.B.C

通过这样的组织方式,可以很好地处理各种包导入引起的问题,比如使用了相对路径导入.

总结:

如果是一个软件包目录A,则创建 A/__main__.py ,在 python -m A 的时候寻找 A/__main__.py如果是一个软件包目录A,则创建 A/__init__.py ,在 import A 的时候会寻找 A/__init__.py如果是一个软件包子目录,则创建 A/B/__main__.py ,在 python -m A.B 的时候寻找 A/B/__mian__.py如果是一个软件包子目录,则创建 A/B/__init__.py ,在 import A.B 的时候寻找 A/B/__init__.py如果是一个模块C,则不管是 import A.B.C 或者 python -m A.B.C 寻找具体的 A/B/C.py

5 文档目录

最好提供 doc/ 目录用于存放文档,至少应该有一个 README .

6 其它常见问题

不要以设置 PYTHONPATH 变量的方式让程序运行;只要软件包组织合理,不需要这样。不要在代码中修改 sys.path项目根目录里面应该包含一个Python软件包,而不是以一个软件包作为项目根目录,否则setup.py可能无法工作。比如软件包A,存放源码的地方可能会是 A/lib/A/src/, 这样都不好,应该采用 A/A/. 如pip的文件结构:

pip/AUTHORS.txtpip/CHANGES.txtpip/LICENSE.txtpip/MANIFEST.inpip/README.rstpip/contrib/pip/docs/pip/setup.cfgpip/setup.pypip/tasks/pip/tests/pip/tox.inipip/pip/__init__.pypip/pip/__main__.pypip/pip/_vendor/pip/pip/basecommand.pypip/pip/baseparser.pypip/pip/cmdoptions.pypip/pip/commands/pip/pip/compat/pip/pip/download.pypip/pip/exceptions.pypip/pip/index.pypip/pip/locations.pypip/pip/pep425tags.pypip/pip/req/pip/pip/status_codes.pypip/pip/utils/pip/pip/vcs/pip/pip/wheel.py
7 pip/pip/__main__.py

from __future__ import absolute_importimport sys# If we are running from a wheel, add the wheel to sys.path# This allows the usage python pip-*.whl/pip install pip-*.whlif __package__ == '':    import os    # __file__ is pip-*.whl/pip/__main__.py    # first dirname call strips of '/__main__.py', second strips off '/pip'    # Resulting path is the name of the wheel itself    # Add that to sys.path so we can import pip    path = os.path.dirname(os.path.dirname(__file__))    sys.path.insert(0, path)import pipif __name__ == '__main__':    sys.exit(pip.main())
8 后记

遵循这几条简单地规则,就可以在创建Python软件包的时候省去很多烦恼.

9 资料

http://blog.habnab.it/blog/2013/07/21/python-packages-and-you/http://stackoverflow.com/questions/72852/how-to-do-relative-imports-in-pythonhttp://legacy.python.org/dev/peps/pep-0366/


作者简介:

朱春来(Leslie Zhu),金融工程师,毕业于西安电子科技大学, 喜欢历史,喜欢编程. 日常在GNU/Linux环境下进行C/C++、Python开发,对Common Lisp、Node.js、金融等感兴趣。可以通过邮箱(pythonisland@gmail.com)联系他,或者直接在他的个人主页上留言.

访问朱春来(Leslie Zhu)的个人主页(http://lesliezhu.github.com)

如何组织Python软件包

相关文章:

你感兴趣的文章:

标签云: