当使用同一字典用于成员值时,Python枚举显示出奇怪的行为



我不明白为什么当我将dict分配为每个成员的值时,这个枚举没有我定义的所有成员:

from enum import Enum
class Token(Enum):
    facebook = {
    'access_period': 0,
    'plan_name': ''}
    instagram = {
    'access_period': 0,
    'plan_name': ''}
    twitter = {
    'access_period': 0,
    'plan_name': ''}
if __name__ == "__main__":
    print(list(Token))

输出为:

[<Token.twitter: {'plan_name': '', 'access_period': 0}>]

…但是我期望的是:

[<Token.facebook:  {'plan_name': '', 'access_period': 0}>,
 <Token.instagram: {'plan_name': '', 'access_period': 0}>,
 <Token.twitter:   {'plan_name': '', 'access_period': 0}>]

为什么没有显示所有成员?

枚举为成员执行唯一值。成员定义与其他定义相同的值将被视为别名。

演示:

Token.__members__
# OrderedDict([('twitter',
#               <Token.twitter: {'plan_name': '', 'access_period': 0}>),
#              ('facebook',
#               <Token.twitter: {'plan_name': '', 'access_period': 0}>),
#              ('instagram',
#               <Token.twitter: {'plan_name': '', 'access_period': 0}>)])
assert Token.instagram == Token.twitter

定义的名称都存在,但是它们都映射到同一成员。

如果您有兴趣,请查看源代码:

# [...]
# If another member with the same value was already defined, the
# new member becomes an alias to the existing one.
for name, canonical_member in enum_class._member_map_.items():
    if canonical_member._value_ == enum_member._value_:
        enum_member = canonical_member
        break
else:
    # Aliases don't appear in member names (only in __members__).
    enum_class._member_names_.append(member_name)
# performance boost for any member that would not shadow
# a DynamicClassAttribute
if member_name not in base_attributes:
    setattr(enum_class, member_name, enum_member)
# now add to _member_map_
enum_class._member_map_[member_name] = enum_member
try:
    # This may fail if value is not hashable. We can't add the value
    # to the map, and by-value lookups for this value will be
    # linear.
    enum_class._value2member_map_[value] = enum_member
except TypeError:
    pass
# [...]

此外,在我看来,您想利用枚举类以在运行时修改值(字典(。对于其他人使用您的代码阅读/阅读/使用您的代码,这是强烈灰心的,并且非常不直觉。预计枚举将由常数制成。

如@michaelhoff所指出的,Enum的行为是考虑具有相同值的名称为Aliases 1

您可以使用Advanced Enum 2 库来解决此问题:

from aenum import Enum, NoAlias
class Token(Enum):
    _settings_ = NoAlias
    facebook = {
        'access_period': 0,
        'plan_name': '',
        }
    instagram = {
        'access_period': 0,
        'plan_name': '',
        }
    twitter = {
        'access_period': 0,
        'plan_name': '',
        }
if __name__ == "__main__":
    print list(Token)

输出现在是:

[
  <Token.twitter: {'plan_name': '', 'access_period': 0}>,
  <Token.facebook: {'plan_name': '', 'access_period': 0}>,
  <Token.instagram: {'plan_name': '', 'access_period': 0}>,
  ]

加强迈克尔所说的话:Enum成员是常数 - 除非您真的知道自己在做什么,否则您不应该使用非恒定值。


使用NoAlias的更好示例:

class CardNumber(Enum):
    _order_ = 'EIGHT NINE TEN JACK QUEEN KING ACE'  # only needed for Python 2.x
    _settings_ = NoAlias
    EIGHT    = 8
    NINE     = 9
    TEN      = 10
    JACK     = 10
    QUEEN    = 10
    KING     = 10
    ACE      = 11

1 请参阅标准Enum使用的答案。

2 披露:我是Python STDLIB Enumenum34 Backport和Advanced Eneumeration(aenum(库的作者。

最新更新