创建一个软件包(package)似乎已经足够简单了,也就是在文件目录下搜集一些模块,再加上一个__init__.py文件,对吧?我们很容易看出来,随着时间的推移,通过对软件包的越来越多的修改,一个设计很差的软件包可能会出现循环依赖问题,或是可能变得不可移植和不可靠。
1. __init__.py 仅为导入服务
对于一个简单的软件包,你可能会忍不住把工具方法,工厂方法和异常处理都丢进__init__.py,千万别这样!
一个结构良好的__init__.py文件,仅为一个非常重要的目的来服务:从子模块导入。你的__init__.py应该看起来像这个样子:
# ORDER MATTERS HERE -- SOME MODULES ARE DEPENDANT ON OTHERS# 导入顺序要考虑——一些模块会依赖另外的一些from exceptions import FSQError, FSQEnvError, FSQEncodeError, FSQTimeFmtError, FSQMalformedEntryError, FSQCoerceError, FSQEnqueueError, FSQConfigError, FSQPathError, FSQInstallError, FSQCannotLockError, FSQWorkItemError, FSQTTLExpiredError, FSQMaxTriesError, FSQScanError, FSQDownError, FSQDoneError, FSQFailError, FSQTriggerPullError, FSQHostsError, FSQReenqueueError, FSQPushError # constants relies on: exceptions, internalimport constants # const relies on: constants, exceptions, internalfrom const import const, set_const # has tests # path relies on: exceptions, constants, internalimport path # has tests # lists relies on: pathfrom lists import hosts, queues #...
2.使用__init__.py来限制导入顺序
把方法和类置于软件包的作用域中,这样用户就不需要深入软件包的内部结构,使你的软包变得易用。 作为调和导入顺序的唯一地方。使用得当的话,__init__.py 可以为你提供重新组织内部软件包结构的灵活性,而不需要担心由内部导入子模块或是每个模块导入顺序所带来的副作用。因为你是以一个特定的顺序导入子模块,你的__init__.py 对于他程序员来讲应该简单易懂,并且能够明显的表示该软件包所能提供的全部功能。
文档字符串,以及在软件包层面对__all__属性的赋值应当是__init__.py中唯一的与导入模块不相关的代码:
__all__ = [ 'FSQError', 'FSQEnvError', 'FSQEncodeError', 'FSQTimeFmtError', 'FSQMalformedEntryError', 'FSQCoerceError', 'FSQEnqueueError', 'FSQConfigError', 'FSQCannotLock', 'FSQWorkItemError', 'FSQTTLExpiredError', 'FSQMaxTriesError', 'FSQScanError', 'FSQDownError', 'FSQDoneError', 'FSQFailError', 'FSQInstallError', 'FSQTriggerPullError', 'FSQCannotLockError', 'FSQPathError', 'path', 'constants', 'const', 'set_const', 'down', 'up', # ... ]
3.使用一个模块来定义所有的异常
你也许已经注意到了,__init__.py中的第一个导入语句从exceptions.py子模块中导入了全部的异常。从这里出发,你将看到,在大多数的软件包中,异常被定义在引起它们的代码附近。尽管这样可以为一个模块提供高度的完整性,一个足够复杂的软件包会通过如下两种方式,使得这一模式出现问题。
新闻热点
疑难解答