处理大量具有多个语义的常量的最佳方法

时间:2014-07-25 14:13:26

标签: python

我打算实现一个包含大量常量的库。现在已经有很多关于stackoverflow常量的问题了。但我正在思考一个使事情变得复杂的特殊情况,我不知道该走哪条路。可能有人已经解决了类似的问题,可以帮助我避免明显的失败:)。

更确切地说,我将实现SMPP(短消息p2p)相关的东西。 SMPP由许多具有不同可能值的字段组成,这些值有时由不同部分组成。 esm_class字段例如是8位整数,位1-0,5-2和7-6是“子字段”。所以esm_class的值由三个常量“连接”组成。

但故事还在继续。 esm_class子字段具有不同的含义,具体取决于它们使用的消息类型。

现在出现了几个问题:

  1. 组织不同常数的最佳方法是什么?
  2. 如何为同一个字段(或子字段)定义不同的常量?
  3. 为每个常量添加描述的最佳方法是什么?
  4. 对于问题3,我选择了tupels:CONST_NAME =(0x01,'value meaning',)。

    我对其他问题的第一个想法是在模块中组织常量。例如,这将给我们:

    • constants.esm_class,其中包含所有esm_class值
    • 包含所有可能编码的constants.data_coding
    • etc(对于每个字段,常量中的模块)

    对于data_coding,这很有效。但是esm_class实际上是由“子字段”组成的。所以我想到了:

    • 消息类型子字段的constants.esm_class.types
    • 消息模式的constants.esm_class.modes
    • feature字段的constants.esm_class.features

    这已经很长了,导致例如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.这就是我所说的“子类型”。常量将与按位运算放在一起。

1 个答案:

答案 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)

现在,代码当然可以被优化,但重点是:我试图尽快抽象出二进制表示,以便得到表示协议中出现的结构的类层次结构。因此,我并没有简单地将常量/值放在不同的模块中,而是将它们组织起来,使它们与它们所属的结构一起出现 - 这也是你将要使用的结构在你的其余代码中。

还有一点需要注意:

  • 如果您愿意,您当然可以省略类FeaturesModesTypes,并将所有值放在esm_class的范围内。但是,我只会在符号&#39;名称FEATURE_1MODE_2等表明它们对应的位串的哪一部分(即特征部分或模式部分或......)。

  • 我通常会避免使用Python 3.4的枚举,因为当您需要访问符号的值(即其value属性)时,IMO处理它们很繁琐但是#39;也因为我不需要符号&#39;自称。您的里程可能会有所不同。

  • 最后,在上面的评论中,chepner对是否在代码中存储描述提出了非常好的观点。我建议你听从他的建议。