创建一个软件包(package)似乎已经足够简单了,也就是在文件目录下搜集一些模块,再加上一个__init__.py文件,对吧?我们很容易看出来,随着时间的推移,通过对软件包的越来越多的修改,一个设计很差的软件包可能会出现循环依赖问题,或是可能变得不可移植和不可靠。
__init__.py 仅为导入服务对于一个简单的软件包,你可能会忍不住把工具方法,工厂方法和异常处理都丢进__init__.py,千万别这样!
一个结构良好的__init__.py文件,仅为一个非常重要的目的来服务:从子模块导入。你的__init__.py应该看起来像这个样子:
| 123456789101112131415161718192021222324 | # 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 #… |
__init__.py来限制导入顺序使用得当的话,__init__.py 可以为你提供重新组织内部软件包结构的灵活性,而不需要担心由内部导入子模块或是每个模块导入顺序所带来的副作用。因为你是以一个特定的顺序导入子模块,你的__init__.py 对于他程序员来讲应该简单易懂,并且能够明显的表示该软件包所能提供的全部功能。
文档字符串,以及在软件包层面对__all__属性的赋值应当是__init__.py中唯一与导入无关的代码:
| 123456789 | __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\’, # … ] |
你也许已经注意到了,__init__.py中的第一个导入语句从exceptions.py子模块中导入了全部的异常。从这里出发,你将看到,在大多数的软件包中,异常被定义在引起它们的代码附近。尽管这样可以为一个模块提供高度的完整性,一个足够复杂的软件包会通过如下两种方式,使得这一模式出现问题。
你应该为你的软件包的异常定义一个基类:
| 123 | class APackageException(Exception): \’\’\’root for APackage Exceptions, only used to except any APackage error, never raised\’\’\’ pass |
然后确保你的软件包在任何错误状态下,只会引发这个基类异常的子类异常,这样如果你需要的话,你就可以阻止全部的异常:
| 1234 | try: \’\’\’bunch of code from your package\’\’\’except APackageException: \’\’\’blanked condition to handle all errors from your package\’\’\’ |
对于一般的错误状态,这里有一些重要的异常处理已经被包括在标准库中了(例如,TypeError, ValueError等)
灵活地定义异常处理并保持足够的粒度:
| 12345678910 | # from fsqclass FSQEnvError(FSQError): \’\’\’An error if something cannot be loaded from env, or env has an invalid value\’\’\’ passom/category/feedback/trans-team/\”>翻译组。
创建一个软件包(package)似乎已经足够简单了,也就是在文件目录下搜集一些模块,再加上一个 1.
|
| 123456789101112131415161718192021222324 | # 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 #… |
__init__.py来限制导入顺序使用得当的话,__init__.py 可以为你提供重新组织内部软件包结构的灵活性,而不需要担心由内部导入子模块或是每个模块导入顺序所带来的副作用。因为你是以一个特定的顺序导入子模块,你的__init__.py 对于他程序员来讲应该简单易懂,并且能够明显的表示该软件包所能提供的全部功能。
文档字符串,以及在软件包层面对__all__属性的赋值应当是__init__.py中唯一与导入无关的代码:
| 123456789 | __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\’, # … ] |
你也许已经注意到了,__init__.py中的第一个导入语句从exceptions.py子模块中导入了全部的异常。从这里出发,你将看到,在大多数的软件包中,异常被定义在引起它们的代码附近。尽管这样可以为一个模块提供高度的完整性,一个足够复杂的软件包会通过如下两种方式,使得这一模式出现问题。
你应该为你的软件包的异常定义一个基类:
| 123 | class APackageException(Exception): \’\’\’root for APackage Exceptions, only used to except any APackage error, never raised\’\’\’ pass |
然后确保你的软件包在任何错误状态下,只会引发这个基类异常的子类异常,这样如果你需要的话,你就可以阻止全部的异常:
| 1234 | try: \’\’\’bunch of code from your package\’\’\’except APackageException: \’\’\’blanked condition to handle all errors from your package\’\’\’ |
对于一般的错误状态,这里有一些重要的异常处理已经被包括在标准库中了(例如,TypeError, ValueError等)
灵活地定义异常处理并保持足够的粒度:
| 12345678910 | # from fsqc |