如何方便地定义命名整数常量列表?

时间:2017-04-19 17:17:06

标签: python dictionary global-variables config constants

我想定义常量,例如A = 10; B = 20,具有以下属性:

  1. '正常'访问:例如A.value + B.value == 30(甚至A + B == 30
  2. 允许重复值:例如A = 10; B = 10
  3. 每个变量的关联字符串:例如A.name == "A"
  4. 清单:例如[v.name for v in CONSTANTS]
  5. IDE支持:自动完成名称,重新分解等
  6. 添加新常量需要一次更改
  7. 可输入:例如from config import CONSTANTS
  8. 最后,属性#6是问题所在。像

    这样的东西
    A = 10
    B = 20
    
    A_NAME = "A"
    B_NAME = "B"
    
    CONSTANTS = { A_NAME: A, B_NAME: B }
    

    基本上就是解决方案。但我想避免使用这个冗长的代码(如果可能的话)。

    这种尝试非常接近:

    import enum
    
    class CONSTANTS(enum.IntEnum):
        A = 10
        B = 20
    
    assert CONSTANTS.A + CONSTANTS.B == 30
    assert CONSTANTS.A.name == "A"
    assert CONSTANTS.__members__.keys() == ["A", "B"]
    assert CONSTANTS.__members__.values() == [10, 20]
    

    但是当两个常量具有相同的值(属性#2)时失败。还可以使用Enum构造函数定义value的子类。但同样,A = 10; B = 10将映射到同一个实例,即使名称不同。现在,人们还可以添加一个名称(或任何其他未使用的,但与构造函数不同的参数),以使这个问题消失。但我认为这种做法不洁净。例如,A = ("A", 10); B = ("B", 10); def __init__(self, _, value): ...

3 个答案:

答案 0 :(得分:0)

使用globals()将dict内容复制到全局变量中,可以使CONSTANTS dict更简洁:

CONSTANTS = {"A": 10, "B": 20}
globals().update(CONSTANTS)

print(B)  # prints 20

答案 1 :(得分:0)

您是否考虑过使用namedtuple集合?

from collections import namedtuple

IntConst = namedtuple('IntConst', ['name', 'value'])

A = IntConst('A',10)
B = IntConst('B',20)

他们满足您的正常访问"按字段......

A.value + B.value

当多个IntConst具有相同的值时,他们不会抱怨...

A = IntConst('A',10)
B = IntConst('B',10)

他们会给出一个相关的字符串......

A.name

他们将允许列出......

CONSTANTS = list()
CONSTANTS.extend([A,B])
[v.name for v in CONSTANTS]

您的IDE可能已经支持namedtuple。添加更多常量只需要您创建IntConst namedtuple的实例。对于导入,您只需在命名空间中创建这些IntConst值的列表,它们就会成为import语句的有效目标。

他们唯一没有给出你要求的是能够从中输入整数值的能力。相反,您将获得包含IntConst个实例的元组。

>>> A+B
('A', 10, 'B', 10)

答案 2 :(得分:0)

免责声明:不鼓励访问隐藏属性,因此不应将其视为“干净”的解决方案!

标准枚举模块可以调整以提供所需的行为。

import enum

class Const1(enum.Enum):
    A = 42
    B = 42

    def __init__(self, size):
        self._value_ = (self.name, size)
        self.size = size

class Const2(enum.IntEnum):
    A = 42
    B = 42

    def __init__(self, size):
        enum.IntEnum.__init__(self)
        self._value_ = (self.name, size)

使用成员名称填充_value_属性,使alias lookup mechanism无法找到具有相同值的其他成员。唯一的缺点是生成成员的value属性都引用其内部_value_属性。实际上,value被定义为DynamicClassAttribute。因此,如果不进行进一步修改,我们将面临:Const1.A.value == ('A', 42)

如果需要,可以使用属性覆盖此行为。

@property
def value(self):
    return self._value_[1]