构建健壮 Python 包的 5 个简单规则
admin
2023-07-30 22:10:13
0

创建一个软件包(package)似乎已经足够简单了,也就是在文件目录下搜集一些模块,再加上一个__init__.py文件,对吧?我们很容易看出来,随着时间的推移,通过对软件包的越来越多的修改,一个设计很差的软件包可能会出现循环依赖问题,或是可能变得不可移植和不可靠。

1. __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 #…

2.使用__init__.py来限制导入顺序

  1. 把方法和类置于软件包的作用域中,这样用户就不需要深入软件包的内部结构,使你的软包变得易用。
  2. 作为调和导入顺序的唯一地方。

使用得当的话,__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\’,            # …          ]

3.使用一个模块来定义所有的异常

你也许已经注意到了,__init__.py中的第一个导入语句从exceptions.py子模块中导入了全部的异常。从这里出发,你将看到,在大多数的软件包中,异常被定义在引起它们的代码附近。尽管这样可以为一个模块提供高度的完整性,一个足够复杂的软件包会通过如下两种方式,使得这一模式出现问题。

  1. 通常一个模块/程序需要从一个子模块导入一个函数, 利用它导入代码并抛出异常。为了捕获异常并保持一定的粒度,你需要导入你需要的模块,以及定义了异常的模块(或者更糟,你要导入一系列的异常)。这一系列衍生出来的导入需求,是在你的软件包中编织一张错综复杂的导入之网的始作俑者。你使用这种方式的次数越多,你的软件包内部就变的越相互依赖,也更加容易出错。
  2. 随着异常数量的不断增长,找到一个软件包可能引发的全部异常变的越来越难。把所有的异常定义在一个单独的模块中,提供了一个方便的地方,在这里,程序员可以审查并确定你的软件包所能引发全部潜在错误状态。

你应该为你的软件包的异常定义一个基类:

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)似乎已经足够简单了,也就是在文件目录下搜集一些模块,再加上一个__init__.py文件,对吧?我们很容易看出来,随着时间的推移,通过对软件包的越来越多的修改,一个设计很差的软件包可能会出现循环依赖问题,或是可能变得不可移植和不可靠。

1. __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 #…

2.使用__init__.py来限制导入顺序

  1. 把方法和类置于软件包的作用域中,这样用户就不需要深入软件包的内部结构,使你的软包变得易用。
  2. 作为调和导入顺序的唯一地方。

使用得当的话,__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\’,            # …          ]

3.使用一个模块来定义所有的异常

你也许已经注意到了,__init__.py中的第一个导入语句从exceptions.py子模块中导入了全部的异常。从这里出发,你将看到,在大多数的软件包中,异常被定义在引起它们的代码附近。尽管这样可以为一个模块提供高度的完整性,一个足够复杂的软件包会通过如下两种方式,使得这一模式出现问题。

  1. 通常一个模块/程序需要从一个子模块导入一个函数, 利用它导入代码并抛出异常。为了捕获异常并保持一定的粒度,你需要导入你需要的模块,以及定义了异常的模块(或者更糟,你要导入一系列的异常)。这一系列衍生出来的导入需求,是在你的软件包中编织一张错综复杂的导入之网的始作俑者。你使用这种方式的次数越多,你的软件包内部就变的越相互依赖,也更加容易出错。
  2. 随着异常数量的不断增长,找到一个软件包可能引发的全部异常变的越来越难。把所有的异常定义在一个单独的模块中,提供了一个方便的地方,在这里,程序员可以审查并确定你的软件包所能引发全部潜在错误状态。

你应该为你的软件包的异常定义一个基类:

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

相关内容

热门资讯

500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
Prometheus+Graf... 一,Prometheus概述 1,什么是Prometheus?Prometheus是最初在Sound...
python绘图库Matplo... 本文简单介绍了Python绘图库Matplotlib的安装,简介如下: matplotlib是pyt...