如何在内部跟踪Python枚举值?
我的最终目标实际上是在我的Enum类中创建一个helper方法,该方法总是返回一个Enum成员,并且在给定任何可能的值(例如如何在内部跟踪Python枚举值?,python,python-3.x,enums,Python,Python 3.x,Enums,我的最终目标实际上是在我的Enum类中创建一个helper方法,该方法总是返回一个Enum成员,并且在给定任何可能的值(例如 Color.from_value('red') 如果该值不是枚举的一部分,则helper方法将返回默认值,例如Color.UNKNOWN 基于此,我可以通过一些内置成员列出这些值来检查这些值。但是,我接下来要做的是跟踪内部成员中的所有值,这样我就不必每次调用helper方法时都遍历这些值。我试着做了如下类似的事情: class Color(Enum): RED
Color.from_value('red')
如果该值不是枚举的一部分,则helper方法将返回默认值,例如Color.UNKNOWN
基于此,我可以通过一些内置成员列出这些值来检查这些值。但是,我接下来要做的是跟踪内部成员中的所有值,这样我就不必每次调用helper方法时都遍历这些值。我试着做了如下类似的事情:
class Color(Enum):
RED = 'red'
BLUE = 'blue'
GREEN = 'green'
# this doesn't work
_values = [item.value for item in Color]
正如所料,它不起作用。这可能是Python枚举中内置的吗?您可以创建方法并检查类中的值:
import enum
class Color(enum.Enum):
RED = 'red'
BLUE = 'blue'
GREEN = 'green'
UNKNOWN = "unknown"
@classmethod
def from_value(cls, value):
try:
return {item.value: item for item in cls}[value]
except KeyError:
return cls.UNKNOWN
print(Color.from_value("hey"))
print(Color.from_value("red"))
结果:
Color.UNKNOWN
Color.RED
给你一个
如果您不想重复,您可以始终拥有值的外部缓存:
class Color(enum.Enum):
RED = 'red'
BLUE = 'blue'
GREEN = 'green'
UNKNOWN = "unknown"
@classmethod
def from_value(cls, value):
try:
return _COLORITEMS[value]
except KeyError:
return cls.UNKNOWN
_COLORITEMS = {item.value: item for item in Color}
我已经接受了,但我添加了这一点作为一个从原来的变化。我想保留答案作为参考
import enum
class Color(enum.Enum):
RED = 'red'
BLUE = 'blue'
GREEN = 'green'
UNKNOWN = "unknown"
@staticmethod
def from_value(value):
try:
return Color(value)
except ValueError:
return cls.UNKNOWN
注意,在这种方法中,我将@classmethod
更改为@staticmethod
,因为我不再需要cls
参数。此外,我在这里处理ValueError
而不是KeyError
,因为在找不到值的情况下,Color()
会引发这个问题。这也适用于多值/元组值枚举
关于第二种办法:
import enum
class Color(enum.Enum):
RED = 'red'
BLUE = 'blue'
GREEN = 'green'
UNKNOWN = "unknown"
@staticmethod
def from_value(value):
return Color(value) if value in _COLORITEMS else Color.UNKNOWN
_COLORITEMS = [item.value for item in Color]
在这里,我从dict切换到list来跟踪值。我没有处理
KeyError
,而是简单地检查该值是否在列表中。我本可以使用集合,但因为它是枚举,所以值保证是唯一的(如果我理解正确)。您可以在内部跟踪值,但这有点麻烦:
尝试1
问题1
问题2
它实际上不是常量,因为您仍然可以附加到列表中——但这可以通过强制转换到tuple
来解决:
_values = Constant(tuple([k for k in vars().keys() if not k.startswith('_')]))
然而,这仍然不能解决未知的问题
解决方案
使用Python 3.6或1,您可以指定一个\u missing\u
方法,该方法将被调用,以便在引发ValueError
之前为类提供最后一次机会:
class Constant: # use Constant(object) if in Python 2
def __init__(self, value):
self.value = value
def __get__(self, *args):
return self.value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
class Color(Enum):
RED = 'red'
BLUE = 'blue'
GREEN = 'green'
UNKNOWN = 'unknown'
_values = Constant(tuple([k for k in vars().keys() if not k.startswith('_')]))
@classmethod
def _missing_(cls, name):
if name.lower() in cls._values:
return cls(name.lower())
else:
return cls.UNKNOWN
或
注意:\u missing.
只应返回枚举成员或无
——如果返回任何其他内容,则稍后的Python版本将引发类型错误
1披露:我是、和库的作者。那么如果有人调用Color.from_value('chucknorris')
,会发生什么?这会创建一个新的枚举值并返回它吗?不会。它将返回一个默认枚举,类似于Color.UNKNOWN
.ha!我正要提出第二种变体。但是你更快!因此,从技术上讲,无法在内部跟踪枚举类中的值?@PsychoPunch,我不知道如何跟踪,因为在完全读取之前,您无法访问类值(AFAIK)。谢谢,但我不确定您为什么将其标记为重复,因为我的问题实际上是关于其他内容。虽然您的解决方案可能同时适用于这两种情况,而且这些问题相互交叉,但它们并不完全相同。请看,我还有一个要求,即from_value
不区分大小写,以便from_value('red')
或from_value('red')
返回Color.red
。我如何通过使用\u missing\u
来实现这一点?classmethod
在这里是更好的选择,因为该方法实际上需要类才能正常工作。通过直接指定类名,您可以避开这个问题,但一旦更改名称颜色
,这将失败。使用静态方法
会产生误导。
_values = Constant([k for k in vars().keys() if not k.startswith('_')])
_values = Constant(tuple([k for k in vars().keys() if not k.startswith('_')]))
class Constant: # use Constant(object) if in Python 2
def __init__(self, value):
self.value = value
def __get__(self, *args):
return self.value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
class Color(Enum):
RED = 'red'
BLUE = 'blue'
GREEN = 'green'
UNKNOWN = 'unknown'
_values = Constant(tuple([k for k in vars().keys() if not k.startswith('_')]))
@classmethod
def _missing_(cls, name):
if name.lower() in cls._values:
return cls(name.lower())
else:
return cls.UNKNOWN
class Color(Enum):
RED = 'red'
BLUE = 'blue'
GREEN = 'green'
UNKNOWN = 'unknown'
@classmethod
def _missing_(cls, name):
if name == name.lower():
# avoid infinite recursion
return cls.UNKNOWN
else:
return cls(name.lower())