我打算实现一个包含大量常量的库。现在已经有很多关于stackoverflow常量的问题了。但我正在思考一个使事情变得复杂的特殊情况,我不知道该走哪条路。可能有人已经解决了类似的问题,可以帮助我避免明显的失败:)。
更确切地说,我将实现SMPP(短消息p2p)相关的东西。 SMPP由许多具有不同可能值的字段组成,这些值有时由不同部分组成。 esm_class字段例如是8位整数,位1-0,5-2和7-6是“子字段”。所以esm_class的值由三个常量“连接”组成。
但故事还在继续。 esm_class子字段具有不同的含义,具体取决于它们使用的消息类型。
现在出现了几个问题:
对于问题3,我选择了tupels:CONST_NAME =(0x01,'value meaning',)。
我对其他问题的第一个想法是在模块中组织常量。例如,这将给我们:
对于data_coding,这很有效。但是esm_class实际上是由“子字段”组成的。所以我想到了:
这已经很长了,导致例如constants.esm_class.features.UDHI_AND_REPLAY_PATH。不知何故,不是很好。而这还不是我所需要的......实际上我甚至需要将constants.esm_class.types分开,因为类型值在不同的上下文中具有不同的含义(基本上是传入消息与传出短消息)。因此,如果我遵循模式,这将导致更多的子模块:constants.esm_class.types.outgoing和constants.esm_class.types.incoming
此外,我还需要一种方法来连接类型,模式和功能来创建esm_class值,我还需要拆分esm_class以进行解析。因此,需要有一种通用的方法来从常量构建字段值,并检测某个值是非法的(即,具有未由常量定义的值)。
我的问题:您认为我应该遵循的最佳方式是什么?我应该去上课吗?我已经阅读了python的最佳实践,就是在模块级别定义常量。我也看到了使用类的一些问题。那么每个可能字段的新数据类型可能会这样做吗?我被困住了,期待你的想法!
编辑:我打算使用带有python 3.3+的lib,可能我想让它与2.7兼容,所以很遗憾,Python 3.4中的Enums不是解决方案。
编辑2:可能我在太少的文字中收集了太多信息。所以我举一个例子。
SMPP中的 esm_class是一种派生值,由类型,模式和特征组成。例如,如果esm_class的值为00111100,实际上它意味着该特征等于00,模式为1111,类型再次为00.这就是我所说的“子类型”。常量将与按位运算放在一起。答案 0 :(得分:1)
我会尝试给出答案(我希望我能正确理解你)。根据我上面的评论,我会找到类似的东西:
class esm_class:
BITMASK_FEATURE = 0b11000000
BITMASK_MODE = 0b00111100
BITMASK_TYPE = 0b00000011
class Features:
FEATURE_1 = 0b00
FEATURE_2 = 0b01
FEATURE_3 = 0b10
FEATURE_4 = 0b11
class Modes:
MODE_1 = 0b0000
# …
class Types:
TYPE_1 = 0b00
TYPE_2 = 0b01
# …
def __init__(self, type_, mode, feature):
self.type = type_
self.mode = mode
self.feature = feature
def __bytes__(self):
'''
Use this to write an instance of esm_class to a file-like
object or stream, e.g..:
mysocket.send(bytes(my_esm_class_obj))
'''
return ((self.feature << 6) | (self.mode << 2) |
self.type).to_bytes(1, byteorder='big')
@classmethod
def parse(cls, filelike):
'''
Use this to parse incoming data from a file-like object
and create an instance of esm_class accordingly, e.g:
my_esm_class_obj = esm_class.parse(stream)
'''
instance = cls()
data = filelike.read(1)
instance.feature = int.from_bytes((data & cls.BITMASK_FEATURE) >> 6)
instance.mode = int.from_bytes((data & cls.BITMASK_MODE) >> 2)
instance.mode = int.from_bytes((data & cls.BITMASK_TYPE) >> 2)
现在,代码当然可以被优化,但重点是:我试图尽快抽象出二进制表示,以便得到表示协议中出现的结构的类层次结构。因此,我并没有简单地将常量/值放在不同的模块中,而是将它们组织起来,使它们与它们所属的结构一起出现 - 这也是你将要使用的结构在你的其余代码中。
还有一点需要注意:
如果您愿意,您当然可以省略类Features
,Modes
和Types
,并将所有值放在esm_class
的范围内。但是,我只会在符号&#39;名称FEATURE_1
,MODE_2
等表明它们对应的位串的哪一部分(即特征部分或模式部分或......)。
我通常会避免使用Python 3.4的枚举,因为当您需要访问符号的值(即其value
属性)时,IMO处理它们很繁琐但是#39;也因为我不需要符号&#39;自称。您的里程可能会有所不同。
最后,在上面的评论中,chepner对是否在代码中存储描述提出了非常好的观点。我建议你听从他的建议。