为什么 Python 枚举中允许可变值?
Why are mutable values allowed in Python Enums?
这是 的后续内容。
如果 Enum
的值是可变的(例如 list
s 等),则这些值可以随时更改。如果 Enum
成员是按值检索的,我认为这会带来一些问题,特别是如果有人无意中更改了他查找的 Enum
的值:
>>> from enum import Enum
>>> class Color(Enum):
black = [1,2]
blue = [1,2,3]
>>> val_1 = [1,2]
>>> val_2 = [1,2,3]
>>> Color(val_1)
<Color.black: [1, 2]>
>>> Color(val_2)
<Color.blue: [1, 2, 3]>
>>> my_color = Color(val_1)
>>> my_color.value.append(3)
>>> Color(val_2)
<Color.black: [1, 2, 3]>
>>> Color(val_1)
Traceback (most recent call last):
...
ValueError: [1, 2] is not a valid Color
我认为给定正常的 Python 习语,这是 好的 ,这意味着用户 可以 使用可变变量作为他们的Enum
值,但只是为了了解他们可能打开的蠕虫罐头。
然而,这会带来第二个问题 - 因为您可以按值查找 Enum
成员,并且该值可以是可变的,所以它必须通过 hashmap 以外的方式进行查找/dict
,因为在这样的 dict
.
中,可变不能是 key
将 Enum
值限制为仅不可变类型以便可以使用 dict
实现按值查找会不会更有效(尽管理所当然,灵活性较低)?
我的第二个问题的答案似乎隐藏在 enum.py
的源代码中。
每个 Enum
确实 包含 dict
对 value->member
对 hashable(即不可变的)值,当您按值查找 Enum
时,它会尝试从 dict
中检索成员。如果该值不是 hashable,则它会与所有现有 Enum
值进行蛮力比较是否相等,如果找到匹配则返回该成员。相关代码在enum.py
:
中的第468-476行
try:
if value in cls._value2member_map_:
return cls._value2member_map_[value]
except TypeError:
# not there, now do long search -- O(n) behavior
for member in cls._member_map_.values():
if member._value_ == value:
return member
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
因此,enum.py
的设计者似乎希望在按值获取 Enum
时进行快速查找,但仍希望为 [=12] 提供具有可变值的灵活性=] 值(尽管我仍然想不出 为什么 首先有人会想要它的原因)。
值得强调的是,根据文档,枚举值可以是任何东西。
Note Enum member values Member values can be anything: int, str, etc..
If the exact value is unimportant you may use auto instances and an
appropriate value will be chosen for you. Care must be taken if you
mix auto with other values.
https://docs.python.org/3/library/enum.html#creating-an-enum
它与其他语言的其他枚举实体有很大不同。然而,津贴应该会带来一些有趣的可能性。我喜欢字符串作为值变体,其中在源代码中使用源代码友好的枚举名称,而枚举值可以用于前端代码或控制台应用程序帮助文本或其他内容的演示目的。
这是
如果 Enum
的值是可变的(例如 list
s 等),则这些值可以随时更改。如果 Enum
成员是按值检索的,我认为这会带来一些问题,特别是如果有人无意中更改了他查找的 Enum
的值:
>>> from enum import Enum
>>> class Color(Enum):
black = [1,2]
blue = [1,2,3]
>>> val_1 = [1,2]
>>> val_2 = [1,2,3]
>>> Color(val_1)
<Color.black: [1, 2]>
>>> Color(val_2)
<Color.blue: [1, 2, 3]>
>>> my_color = Color(val_1)
>>> my_color.value.append(3)
>>> Color(val_2)
<Color.black: [1, 2, 3]>
>>> Color(val_1)
Traceback (most recent call last):
...
ValueError: [1, 2] is not a valid Color
我认为给定正常的 Python 习语,这是 好的 ,这意味着用户 可以 使用可变变量作为他们的Enum
值,但只是为了了解他们可能打开的蠕虫罐头。
然而,这会带来第二个问题 - 因为您可以按值查找 Enum
成员,并且该值可以是可变的,所以它必须通过 hashmap 以外的方式进行查找/dict
,因为在这样的 dict
.
key
将 Enum
值限制为仅不可变类型以便可以使用 dict
实现按值查找会不会更有效(尽管理所当然,灵活性较低)?
我的第二个问题的答案似乎隐藏在 enum.py
的源代码中。
每个 Enum
确实 包含 dict
对 value->member
对 hashable(即不可变的)值,当您按值查找 Enum
时,它会尝试从 dict
中检索成员。如果该值不是 hashable,则它会与所有现有 Enum
值进行蛮力比较是否相等,如果找到匹配则返回该成员。相关代码在enum.py
:
try:
if value in cls._value2member_map_:
return cls._value2member_map_[value]
except TypeError:
# not there, now do long search -- O(n) behavior
for member in cls._member_map_.values():
if member._value_ == value:
return member
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
因此,enum.py
的设计者似乎希望在按值获取 Enum
时进行快速查找,但仍希望为 [=12] 提供具有可变值的灵活性=] 值(尽管我仍然想不出 为什么 首先有人会想要它的原因)。
值得强调的是,根据文档,枚举值可以是任何东西。
Note Enum member values Member values can be anything: int, str, etc.. If the exact value is unimportant you may use auto instances and an appropriate value will be chosen for you. Care must be taken if you mix auto with other values. https://docs.python.org/3/library/enum.html#creating-an-enum
它与其他语言的其他枚举实体有很大不同。然而,津贴应该会带来一些有趣的可能性。我喜欢字符串作为值变体,其中在源代码中使用源代码友好的枚举名称,而枚举值可以用于前端代码或控制台应用程序帮助文本或其他内容的演示目的。