Python Enum shows weird behavior when using the same dictionary for member values

I do not understand why this Enum does not have all the members that I defined when I assign dict as the value of each member:

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

Output:

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

... but I was expecting 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 participants shown?

+5
source share
2 answers

Enum provides unique values ​​for members. Member definitions with the same meaning as other definitions will be considered 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 

Certain names all exist, however they are all mapped to the same element.

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

In addition, it seems to me that you want to use the Enum class to change the value (dictionary) at runtime. This is very discouraging, and also very unintuitive for other people reading / using your code. The enumeration is expected to consist of constants.

+5
source

As @MichaelHoff noted , Enum behavior is to treat names with the same values ​​as aliases 1 .

You can get around this using the Advanced Enum 2 library:

 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) 

Exit 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 must be constant - you should not use inconsistent values ​​unless you really know what you are doing.


Best example of using 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 standard use of Enum .

2 Disclosure: I am the author of Python stdlib Enum , enum34 backport , and the Advanced Enumeration ( aenum ) library.

+4
source

Source: https://habr.com/ru/post/1266976/


All Articles