代码之家  ›  专栏  ›  技术社区  ›  Yu Chen little_birdie

直接作为类属性访问python枚举的值

  •  0
  • Yu Chen little_birdie  · 技术社区  · 5 年前

    我正在学习如何使用 Enum 类,并且发现每当我需要访问枚举的实际值时,我需要附加 .value 财产:

    from enum import Enum
    class Pets(Enum):
        DOG = "Fido"
        CAT = "Kitty"
    
    Pets.DOG # yields Pets.DOG
    Pets.DOG.value # yields Fido
    

    作为练习,我正在尝试配置 枚举 类,以便我不需要继续访问 value 财产。 我想要的行为是当我打电话 Pets.DOG 我得到 Fido 作为我的价值。

    我试着用 __getattr_(cls, item) :

    class Pets(Enum):
    
        def __getattr__(self, item):
            print(f"__getattr__ called with {item}")
            return getattr(self, item).value
    
        DOG = "Fido"
        CAT = "Kitty"
    
    
    if __name__ == "__main__":
    
        pets = Pets()
        pets.DOG
    

    但是,我收到 RecursionError: maximum recursion depth exceeded while calling a Python object ,和 item 是字符串值 _value_ . 我不太明白为什么会发生这种行为——这是在Python行为中构建的,还是因为我使用了一个特殊的类 枚举 ?

    我确实看过一篇类似的文章,但是那里的解决方案是使用另一个模块( inspect )或访问 __dict__ dir() 并使用条件或regex的组合自行分析它。有没有更好的方法来访问底层 枚举 价值何在?

    1 回复  |  直到 5 年前
        1
  •  3
  •   Martijn Pieters    5 年前

    如果要将属性映射到字符串,则不要使用枚举类。整体 指向 enum 模块将生成一组表示枚举的单例对象,而不是字符串。从模块文档中:

    枚举是一组绑定到 唯一的常量值 . 在枚举中, 成员可以按身份进行比较 ,并且可以迭代枚举本身。)

    大胆强调我的。 字符串不是唯一的常量值(我可以创建更多 "Fido" 字符串),而不是设计为按标识进行比较(即使 sometimes, for a subset of strings, you can )。

    直接用字符串属性定义自己的类:

    class Pets:
        DOG = "Fido"
        CAT = "Kitty"
    

    您的无限递归错误是由您对该方法的用途的误解引起的。喜欢 all special methods , object.attr 仰视 __getattr__ 对象类型 ,这意味着您的方法适用于 实例 你的 Enum 子类 DOG CAT 属性,而不是类本身,并干扰 EnumMeta 试图测试的元类 _value_ 由您的 阿-格二氏 方法与 self 是新铸造的 Pets.DOG 实例,以及 item 设置为 '_value_' ,然后调用 getattr(Pets.DOG, '_value_') ,哪些调用 阿-格二氏 等等。

    对于你的工作方法,你必须子类化 枚举元 实施 __getattribute__ 在那个子类上( 阿-格二氏 是唯一需要的 丢失的 属性)。但是,考虑到 _获取属性__ 用于 全部的 属性访问,因此必须首先注意检查当前类的实例:

    class EnumDirectValueMeta(EnumMeta):
        def __getattribute__(cls, name):
            value = super().__getattribute__(name)
            if isinstance(value, cls):
                value = value.value
            return value
    
    class Pets(Enum, metaclass=EnumDirectValueMeta):
        DOG = "Fido"
        CAT = "Kitty"
    

    在哪一点 宠物.dog 生产 'Fido' .