簡體   English   中英

當對成員值使用相同的字典時,Python枚舉顯示奇怪的行為

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

當我將一個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))

輸出為:

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

此外,在我看來,您似乎想利用Enum類在運行時修改值(字典)。 不鼓勵這樣做,對於其他閱讀/使用您的代碼的人來說也很不直觀。 枚舉應由常量組成。

正如@MichaelHoff指出的那樣Enum的行為是將具有相同值的名稱視為別名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用法,請參Enum 答案

2披露:我是Python stdlib Enumenum34 backportAdvanced Enumeration( aenum庫的作者。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM