为了账号安全,请及时绑定邮箱和手机立即绑定

如何直接作为类属性访问枚举的值?

如何直接作为类属性访问枚举的值?

aluckdog 2021-11-23 18:10:04
我正在学习如何Enum在 Python 中使用类,并发现每当我需要访问枚举的实际值时,我都需要附加.value属性:from enum import Enumclass Pets(Enum):    DOG = "Fido"    CAT = "Kitty"Pets.DOG # yields Pets.DOGPets.DOG.value # yields Fido作为练习,我正在尝试配置我的Enum类,这样我就不需要不断访问该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 行为,还是因为我使用了一个特殊的类Enum?我确实看过类似的 SO 帖子,但是那里的解决方案是使用另一个模块 ( inspect),或者访问__dict__ordir()并使用条件或正则表达式的组合自己解析它。有没有更好的方法来获取底层证券Enum的价值?
查看完整描述

2 回答

?
交互式爱情

TA贡献1712条经验 获得超3个赞

如果要将属性映射到字符串,请不要使用 enum 类。整点的的enum模块是产生一组表示一个枚举单一对象,而不是串。从模块文档:


枚举是一组绑定到唯一常量值的符号名称(成员)。在枚举中,成员可以通过 identity 进行比较,并且枚举本身可以迭代。)


大胆强调我的。字符串不是唯一的常量值(我可以随意创建更多"Fido"字符串)并且不是为了通过身份进行比较而设计的(即使有时,对于字符串的子集,您可以)。


只需直接使用字符串属性定义您自己的类:


class Pets:

    DOG = "Fido"

    CAT = "Kitty"

您的无限递归错误是由您对该方法用途的误解引起的。像所有的专用方法,object.attr查找__getattr__的对象类型,这里指的是你的方法适用于情况下,你的Enum子类中,DOG和CAT属性在这里,而不是类本身,并与干涉EnumMeta元类试图测试的_value_attibute,这是处理通过您的__getattr__方法self作为新创建的Pets.DOG实例,并item设置为'_value_',然后调用getattr(Pets.DOG, '_value_'),调用__getattr__等。


对于您的工作方法,您必须对该子类进行子类化EnumMeta和实现__getattribute__(__getattr__仅在缺少属性时才调用)。但是,考虑到__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"

此时Pets.DOG产生'Fido'.


查看完整回答
反对 回复 2021-11-23
?
qq_花开花谢_0

TA贡献1835条经验 获得超7个赞

你说,“我想要的行为是,当我打电话时Pets.DOG,我把 Fido 作为我的价值。” CallingPets.DOG真的是 call print(repr(Pets.DOG)),所以我的建议是这样的:


class Pets(enum.Enum):

    DOG = "Fido"

    CAT = "Kitty"

    

    def __repr__(self):

        return self.value

这种方法的一个优点是您仍然可以访问 Enum 的其他功能,例如 Pets.DOG.name


也就是说,我更愿意覆盖__str__而不是__repr__因为这可以实现您避免输入的目标.value,但Enum在使用repr().


我在尝试对我的值使用命名元组而不是简单字符串时遇到了您的问题。在这种情况下,我认为__getattr__是有帮助的,以防万一其他人发现这篇文章有类似的用途,我将包括对我有用的内容:


import enum

import collections


_ = lambda x : x


class XlateEnum(enum.Enum):

    """Enum whose members inherit the attributes of their values, 

        and which applies the assumed function _() for translations. 

    """

    

    def __getattr__(self, name):

        if name == "_value_":

            return enum.Enum.__getattribute__(self, name)

        elif hasattr(self, "_value_") and hasattr(self._value_, name):

                return _(getattr(self._value_, name))

        return enum.Enum.__getattribute__(self, name)

        

    def __setattr__(self, name, new_value):

        if name == "_value_":

            enum.Enum.__setattr__(self, name, new_value)

        elif hasattr(self, "_value_") and hasattr(self._value_, name):

            raise AttributeError("can't set attribute")

        else:

            enum.Enum.__setattr__(self, name, new_value)

            

    def __delattr__(self, name):

        if hasattr(self, "_value_") and hasattr(self._value_, name):

            raise AttributeError("can't delete attribute")

        else:

            enum.Enum.__delattr__(self, name)

            

    def __str__(self):

        return self.str if hasattr(self, "str") else _(enum.Enum.__str__(self.value))

    

class Pet(XlateEnum):

    """My pets. 


    Attributes:

        str: A localized str to name the Pet. How the Pet prints.

        my: A string representing what I call instances of this Pet.

        legs: The int number of legs of normal instances of this Pet.

    """

              

    DOG = collections.namedtuple("PetValue", "str my legs")(_("Dog"), "Fido", 4)

    CAT = collections.namedtuple("PetValue", "str my legs")(_("Cat"), "Kitty", 4)  

    

print(Pet.DOG)    # yields "Dog" (or translated string)

print(Pet.DOG.my) # yields "Fido" (which is not designated for translation)

当然,您可以删除该_()功能。我发现Enum和namedtuple对常量非常有用,因为它使它们彼此保持适当的关系,而且我喜欢将我所有的翻译功能构建到常量本身中,所以这样的代码才有效:


import ipywidgets

ipywidgets.Dropdown(options=Pet) 


查看完整回答
反对 回复 2021-11-23
  • 2 回答
  • 0 关注
  • 207 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信