【问题标题】:Why are mutable values allowed in Python Enums?为什么 Python 枚举中允许可变值?
【发布时间】:2017-04-01 04:26:45
【问题描述】:

这有点像Why are mutable values in Python Enums the same object? 的后续。

如果Enum 的值是可变的(例如lists 等),则可以随时更改这些值。我认为如果 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 实现按值查找不是更有效(虽然,但不太灵活)吗?

【问题讨论】:

    标签: python python-3.x dictionary enums hashable


    【解决方案1】:

    看来我的第二个问题的答案隐藏在enum.py 的源代码中。

    每个Enum 确实 包含一个dictvalue-&gt;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 的设计者希望在按值获取Enums 时快速查找,但仍然希望为Enum 值提供可变值的灵活性(即使我仍然想不出一个原因为什么有人会首先想要那个)。

    【讨论】:

    • 这是一个可变性的用例:假设程序有多个包含一个枚举的退出代码,每个代码都有自己的退出消息(例如CODE = [123, "Exit #123"])。退出情况之一是用户调用脚本错误,因此您需要显示参数引用。问题是引用被封装在ArgumentParser 对象中。所以首先你初始化枚举然后执行几个add_argument()-s,然后做类似的事情:ExitCode.HELP.value[1] += '\n' + args.format_help()。瞧!
    • @hidefromkgb 据我了解您的示例,它适用于元组。
    • 一个用例可能是:``` class Foo(Enum): NO_REPLY = {'name': 'Team Bar', 'email': 'no-reply@bar.com'} 支持= {'name': 'Team Bar', 'email': 'support@bar.com'} ``` 但不知何故我觉得这不好,我正在这样做:``` class Foo(Enum): NO_REPLY支持```,然后保存字典:```地址= {Foo.NO_REPLY:{'name':'Team Bar','email':'no-reply@bar.com'},Foo.SUPPORT:{ '名称':'团队栏','电子邮件':'support@bar.com'}}```
    • 再想一想,我认为我实际上可以使用namedtuple 以更简洁的方式将enum 用于我的用例:``` Address = namedtuple('Address', ['name', 'email']) class Foo(Enum): NO_REPLY = Address('Team Bar', 'no-reply@bar.com') SUPPORT = Address('Team Bar', 'support@bar.com') ```
    【解决方案2】:

    根据文档,值得强调的是枚举值可以是任何东西

    注意枚举成员值 成员值可以是任何值:int、str 等。 如果确切的值不重要,您可以使用自动实例和 将为您选择适当的值。如果你必须小心 将 auto 与其他值混合。 https://docs.python.org/3/library/enum.html#creating-an-enum

    这与其他语言中的其他枚举实体有很大的不同。然而,这个余量应该会带来一些有趣的可能性。我喜欢将字符串作为值变体,在源代码中使用源代码友好的枚举名称,而枚举值可用于表示目的,如前端代码或控制台应用程序帮助文本或其他内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-16
      • 2012-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-05
      • 2011-02-27
      • 1970-01-01
      相关资源
      最近更新 更多