简体   繁体   English

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

[英]Python Enum shows weird behavior when using same dictionary for member values

I don't understand why this Enum doesn't have all the members I defined, when I assign a dict as each member's value: 当我将一个dict分配为每个成员的值时,我不明白为什么此Enum没有我定义的所有成员:

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))

The output is: 输出为:

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

… but I expected something like: …但是我期望的是:

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

Why aren't all the members shown? 为什么不显示所有成员?

Enum enforces unique values for the members. 枚举强制成员的唯一值。 Member definitions with the same value as other definitions will be treated as aliases. 与其他定义具有相同值的成员定义将被视为别名。

Demonstration: 示范:

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

The defined names do all exist, however they are all mapped to the same member. 定义的名称确实存在,但是它们都映射到同一成员。

Have a look at the source code if you are interested: 如果您感兴趣,请看一下源代码

# [...]
# 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
# [...]

Further, it seems to me that you want to exploit the Enum class to modify the value (the dictionary) during run-time. 此外,在我看来,您似乎想利用Enum类在运行时修改值(字典)。 This is strongly discouraged and also very unintuitive for other people reading/using your code. 不鼓励这样做,对于其他阅读/使用您的代码的人来说也很不直观。 An enum is expected to be made of constants. 枚举应由常量组成。

As @MichaelHoff noted , the behavior of Enum is to consider names with the same values to be aliases 1 . 正如@MichaelHoff指出的那样Enum的行为是将具有相同值的名称视为别名1

You can get around this by using the Advanced Enum 2 library: 您可以使用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)

Output is now: 现在的输出是:

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

To reinforce what Michael said: Enum members are meant to be constants -- you shouldn't use non-constant values unless you really know what you are doing. 为了强调迈克尔所说的话: Enum成员本来就是常量-除非您真的知道自己在做什么,否则不应使用非常量值。


A better example of using NoAlias : 使用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 See this answer for the standard Enum usage. 1有关标准Enum用法,请参Enum 答案

2 Disclosure: I am the author of the Python stdlib Enum , the enum34 backport , and the Advanced Enumeration ( aenum ) library. 2披露:我是Python stdlib Enumenum34 backportAdvanced Enumeration( aenum库的作者。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM