Python常量
Contents
与C/C++不同,Python在语法上并没有定义常量,尽管PEP 8定义了常量的命名规范为大写字母和下划线组成。
在实际项目中,常量首次赋值后, 无法阻止其他代码对其进行修改或删除。
现存的办法
幸运的是该问题在2001年就有人给出了解决方案Constants in Python,基本内容如下:
class _const:
class ConstError(TypeError):
pass
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't rebind const instance attribute (%s)" % name
self.__dict__[name] = value
import sys
sys.modules[__name__] = _const()
其大致含义为:
- 通过_const的__setattr__对象方法判断该对象是否存在属性name,若存在则抛出自定义异常ConstError,否则创建该属性。
- 将_const实例化的对象赋值sys.modules[__name__],const模块被绑定成_const对象。__name__在首次载入const过程中为’const’,而sys.modules是模块名与已加载模块的dict。
如何使用const模块呢?
import const
const.magic = 23
若再次赋值const.magic,
const.magic = 88
则将抛出ConstError的异常。
如何避免常量被删除?
实际项目中,常量并不希望被其他代码删除。在_const类中加入:
def __delattr__(self, name):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't unbind const const instance attribute (%s)" % name
raise AttributeError, "const instance has no attribute '%s'" % name
如此,删除已定义的常量(假设const.magic已经赋值):
del const.magic
则将抛出ConstError的异常。
配置文件与常量
实际项目中,为了让应用程序灵活部署,一般会用配置文件存储应用程序的各种参数;而这些参数通常都以常量语义存在于应用程序中。
在上述代码基础上,根据应用程序的实际情况将常量划分为3类:
- 特殊含义的数值或字符串,如 ETC_FSTAB = “/etc/fstab” 。其作用为
- 避免程序中到处出现类似特殊值,因为人为输入特殊值的低级错误将耗费不必要的调试/测试时间
- 另一方面, 神秘数值(magic number),如 LUN_BLOCK_SIZE = 4096,将影响程序的可读性。
- 应用程序参数的默认值,如 URLOPEN_DEFAULT_TIMEOUT = 15 。其作用主要为配置文件参数的默认值。
- 通过配置文件载入的参数,如通过ConfigParser.SafeConfigParser解析形如ini文件的参数。
第1和2类常量作为_const类的类属性,第3类常量可以在__init__方法中初始化。如:
class _const:
ETC_FSTAB = "/etc/fstab"
LUN_BLOCK_SIZE = 4096
URLOPEN_DEFAULT_TIMEOUT = 15
def __init__(self):
conf = ConfigParser.SafeConfigParser()
conf.read(self.CONF_PATH)
try:
self.URLOPEN_TIMEOUT = conf.getint("DEFAULT", "urlopen_timeout")
except:
self.URLOPEN_TIMEOUT = self.URLOPEN_DEFAULT_TIMEOUT
... ...
某些情况下,应用程序可能并不希望const模块在被外部调用时绑定新属性(常量),实现如下:
def __init__(self):
# Constant definition area
_const.__setattr__ = _const._setattr_impl
def _setattr_impl(self, name, value):
raise self.ConstError, "Can't bind const instance attribute (%s)" % name
其原理为_const对象初始化完成后将__setattr__设置为禁止绑定属性的实现。若在_const类中实现__setattr__为禁止绑定属性,则__init__也将无法初始化(绑定)对象属性。
存在的小问题
该问题并不影响该解决方案的使用,具体请参考: