更新:还有一件需要考虑的事情,我认为更好的做法是将子集成员定义为每个枚举成员的属性,例如:
fruit = FRUIT.ORANGE
...
if fruit.is_citrus:
make_juice()
这些可以被定义为类上的@property
,并且不会受到下面提到的可变性问题的影响。
class FRUIT(Enum):
APPLE = 1
BANANA = 2
LEMON = 3
ORANGE = 4
@property
def is_citrus(self):
return self in frozenset((FRUIT.LEMON, FRUIT.ORANGE))
感谢其他回答者提供的非常有用的观点。在考虑其他答案后,这是我最终采取的做法,接下来是我的理由:
from enum import Enum
class FRUIT(Enum):
APPLE = 1
BANANA = 2
LEMON = 3
ORANGE = 4
FRUIT.CITRUS_TYPES = frozenset((FRUIT.LEMON, FRUIT.ORANGE))
这个很好地运作了,也没有破坏其他的“枚举”行为(让我很惊讶)。
# ---CITRUS_TYPES subset has desired behavior---
>>> FRUIT.LEMON in FRUIT.CITRUS_TYPES
True
>>> FRUIT.APPLE in FRUIT.CITRUS_TYPES
False
>>> "foobar" in FRUIT.CITRUS_TYPES
False
# ---CITRUS_TYPES has not become a member of FRUIT enum---
>>> tuple(FRUIT)
(FRUIT.APPLE: 1>, <FRUIT.BANANA: 2>, <FRUIT.LEMON: 3>, <FRUIT.ORANGE: 4>)
>>> FRUIT.APPLE in FRUIT
True
>>> FRUIT.CITRUS_TYPES in FRUIT
DeprecationWarning: using non-Enums in containment checks will raise TypeError in Python 3.8
False
# ---CITRUS_TYPES not reported by dir(FRUIT)---
>>> dir(FRUIT)
['APPLE', 'BANANA', 'LEMON', 'ORANGE', '__class__', '__doc__', '__members__', '__module__']
# ---But it does appear on FRUIT.__dict__---
FRUIT.__dict__ == {
'_generate_next_value_': <function Enum._generate_next_value_ at 0x1010e9268>,
'__module__': '__main__',
'__doc__': 'An enumeration.',
'_member_names_': ['APPLE', 'BANANA', 'LEMON', 'ORANGE'],
'_member_map_': OrderedDict([
('APPLE', <FRUIT.APPLE: 1>),
('BANANA', <FRUIT.BANANA: 2>),
('LEMON', <FRUIT.LEMON: 3>),
('ORANGE', <FRUIT.ORANGE: 4>)
]),
'_member_type_': <class 'object'>,
'_value2member_map_': {
1: <FRUIT.APPLE: 1>,
2: <FRUIT.BANANA: 2>,
3: <FRUIT.LEMON: 3>,
4: <FRUIT.ORANGE: 4>,
},
'APPLE': <FRUIT.APPLE: 1>,
'BANANA': <FRUIT.BANANA: 2>,
'LEMON': <FRUIT.LEMON: 3>,
'ORANGE': <FRUIT.ORANGE: 4>,
'__new__': <function Enum.__new__ at 0x1010e91e0>,
'CITRUS_TYPES': frozenset({<FRUIT.LEMON: 3>, <FRUIT.ORANGE: 4>})
}
看起来它将 CITRUS_TYPES
存储在类上,但出于某些原因,将其隐藏在 dir()
中。
然而,它确实存在漏洞,因为添加的属性是可变的,就像任何其他类属性一样;如果客户端代码的某个部分将 FRUIT.CITRUS_TYPES
赋值,FRUIT
不会抱怨,这当然会破坏事情。这种行为与 Enum
成员不同,后者在尝试赋值时引发 AttributeError
。
我认为这可以通过将其变为 classproperty
来解决,我尝试过,但我的早期尝试没有防止变异。那里描述的更复杂的 classproperty 实现可能有效,但我目前对简单的方法感到满意。
@blhsing提出了一个有趣的问题,即在FRUIT
上使用这样的属性是否有意义。我理解他的观点,也许会采纳他的看法,但我目前的观点是将“与水果相关”的特性局限于单个导入名称最适合我。可以考虑FRUIT
作为严格的水果类型和子集的集合,因此是一个不同的集合。对于我的当前目的,我发现这种严谨性没有什么回报,更喜欢将FRUIT
视为相关常量值的集合,包括成员和子集。当然,你的情况可能不同。就像我说的,我可能会采纳他的观点。
Enum
内置方法冲突,因为这些方法操作该级别下的所有属性。 - Prune