我正在学习如何在Python中使用Enum
类,并且发现,每当我需要访问枚举的实际值时,都需要附加.value
属性:
from enum import Enum
class Pets(Enum):
DOG = "Fido"
CAT = "Kitty"
Pets.DOG # yields Pets.DOG
Pets.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__
或dir()
并自己解析条件或正则表达式的组合。有没有更好的方法来访问基础Enum
的值?
答案 0 :(得分:3)
如果要将属性映射到字符串,请不要使用枚举类。 enum
模块的整个 point 用于产生一组表示枚举而不是字符串的单例对象。从模块文档中:
枚举是绑定到唯一,恒定值的一组符号名称(成员)。在枚举中,成员可以通过身份进行比较,并且枚举本身可以进行迭代。)
强调粗体。字符串不是唯一的,恒定的值(我可以随意创建更多"Fido"
字符串),并且不能按标识进行比较(即使{{3 }}。
只需直接使用字符串属性定义您自己的类:
class Pets:
DOG = "Fido"
CAT = "Kitty"
您的无限递归错误是由于您对该方法的用途有误而引起的。像sometimes, for a subset of strings, you can一样,object.attr
在对象类型上查找__getattr__
,这意味着您的方法适用于{{的实例 1}}子类,这里的Enum
和DOG
属性,而不是类本身,并且干扰了CAT
元类,试图测试EnumMeta
服装,这是由您的_value_
方法处理,其中__getattr__
是新创建的self
实例,并且Pets.DOG
设置为item
,然后调用'_value_'
,会调用getattr(Pets.DOG, '_value_')
,等等。
对于您的工作方法,您必须子集__getattr__
并在该子类上实现all special methods(EnumMeta
仅用于 missing 属性)。但是,请考虑到__getattr__
用于 all 属性访问,因此您必须先检查当前类的实例:
__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
。
答案 1 :(得分:1)
您说过,“我想要的行为是,当我调用 Pets.DOG
时,我将 Fido 作为我的值。”调用 Pets.DOG
确实调用了 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
时的 1}} 成员。
我在尝试使用命名元组而不是简单字符串作为我的值时遇到了您的问题。在这种情况下,我认为 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