Python枚举和异常:TypeError: 尝试重复使用键(key)。

3

当我尝试重新定义一个枚举时,出现错误。

我的代码如下:

from enum import Enum

class FooEnum(Enum):
    try:
        foo = 3/0
    except Exception as my_exception_instance:
        print('An error occurred:', my_exception_instance)
        foo=0

目标是让3/0引发异常,然后重新定义foo。然而,当我按原样运行时,会显示打印消息,但会抛出另一个错误,这对我来说没有意义。以下是输出和堆栈跟踪:
An error occurred: division by zero
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-10-489d2391f28b> in <module>
      1 from enum import Enum
      2 
----> 3 class FooEnum(Enum):
      4     try:
      5         foo = 3/0

<ipython-input-10-489d2391f28b> in FooEnum()
      6     except Exception as my_exception_instance:
      7         print('An error occurred:', my_exception_instance)
----> 8         foo=0

/usr/lib/python3.6/enum.py in __setitem__(self, key, value)
     90         elif key in self._member_names:
     91             # descriptor overwriting an enum?
---> 92             raise TypeError('Attempted to reuse key: %r' % key)
     93         elif not _is_descriptor(value):
     94             if key in self:

TypeError: Attempted to reuse key: 'my_exception_instance'
想要解决这个错误,唯一的方法就是在捕捉异常时不使用它:
from enum import Enum

class FooEnum(Enum):
    try:
        foo = 3/0
    except:
        print('An error occurred')
        foo=0

然后它只会输出:发生了一个错误

我正在使用Python 3.6.9版本


编辑 下面的代码更接近我的用例:

import tensorflow as tf

from enum import Enum

class VisualModels(Enum):
    

    try:
        MobileNet = tf.keras.applications.MobileNetV2
    except Exception as e:
        print(f'MobileNetV2 Not found, using MobileNet instead. Error: {e}.')
        MobileNet = tf.keras.applications.MobileNet

    # more models are defined similarly

1个回答

2
发生这种情况的原因是:
  • _EnumDict 跟踪所有使用的名称
  • _EnumDict 认为 my_exception_instance 应该是成员
  • Python 在离开 except 子句时清除 as 变量
    • 通过将 None 分配给 my_exception_instance(然后删除变量)
    • 导致 _EnumDict 认为正在重复使用键

一个解决方法(截至 Python 3.7)是将 my_exception_instance 添加到 _ignore_1 属性中:

class FooEnum(Enum):
    _ignore_ = 'my_exception_instance'
    try:
        foo = 3/0
    except Exception as my_exception_instance:
        print('An error occurred:', my_exception_instance)
        foo=0

另一个解决方法是将my_exception_instance定义为全局变量:
class FooEnum(Enum):
    global my_exception_instance
    try:
        foo = 3/0
    except Exception as my_exception_instance:
        print('An error occurred:', my_exception_instance)
        foo=0

最后,如果你不想在枚举体中使用 try/except:
class FallbackEnum(Enum):
    def __new__(cls, *values):
        # first actual value wins
        member = object.__new__(cls)
        fallback = False
        for v in values:
            try:
                member._value_ = eval(v)
                break
            except Exception as e:
                print('%s error: %s' % (v, e))
                fallback = True
                continue
        else:
            # never found a value
            raise ValueError('no valid value found')
        # if we get here, we found a value
        if fallback:
            # if the first value didn't work, print the one we did use
            print('  using %s' % v)
        return member

并且在使用中:

>>> class FooEnum(FallbackEnum):
...     foo = '3/0', '0'
... 
3/0 error: division by zero
  using 0

>>> list(FooEnum)
[<FooEnum.foo: 0>]

1 如果你卡在 Python 3.6 上,可以使用 aenum

声明:我是Python标准库Enumenum34回溯高级枚举(aenum)库的作者。


所以,这是一个 bug,对吧?我同意这是一个奇怪的用例,但这是一个公平的用例。在我的情况下,我正在从可能不存在的模块中导入一个函数。对于这些情况,我需要使用默认值。我知道可以通过先询问模块是否存在来完成此操作,但我不明白为什么它不能工作。感谢您的回答。 - Rodrigo Laguna
@RodrigoLaguna:你能添加一个更像你实际代码的例子吗? - Ethan Furman
你的评论非常有用。最终我使用了我提供的第二个选项来避免打印。我将更新我的问题以展示更接近我的代码版本。我应该向枚举模块报告这个问题吗?提前致谢。 - Rodrigo Laguna
@RodrigoLaguna:请继续报告它。我不确定它是否会被修复,因为它超出了“枚举”的预期使用范围,任何“修复”都将是脆弱的。但是,我相信我们可以找到更好的解决方法(可能使用“忽略”)。 - Ethan Furman
@RodrigoLaguna:我更新了我的答案,并提供了一些你现在可以使用的解决方法。 - Ethan Furman
1
@RodrigoLaguna:最终更新:添加了一个自定义枚举,您可能会更喜欢它。 - Ethan Furman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接