【问题标题】:Python Enum with exception: TypeError: Attempted to reuse key:Python枚举异常:TypeError:尝试重用密钥:
【发布时间】:2021-10-14 15:33:27
【问题描述】:

我正在尝试在枚举失败时重新定义它,但随后会引发错误。

我的代码如下所示:

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

然后它只是输出:An error occurred

我正在使用 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

【问题讨论】:

    标签: python exception enums python-3.6


    【解决方案1】:

    发生的原因是:

    • _EnumDict 跟踪所有使用的名称
    • _EnumDict 认为 my_exception_instance 应该是会员
    • 离开except 子句时,Python 会清除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 stdlib Enumenum34 backportAdvanced Enumeration (aenum) 库的作者。

    【讨论】:

    • 所以,这是一个错误,对吧?我同意这是一个奇怪的用例,但这是一个公平的用例。就我而言,我正在从一个可能不存在的模块中导入一个函数。对于这些情况,我需要使用默认值。我知道这可以通过首先询问模块是否存在来完成,但我不明白为什么它不应该工作。谢谢你的回答。
    • @RodrigoLaguna:您能添加一个更像您的实际代码的示例吗?
    • 您的评论非常有用。最后我用我给出的第二个选项避免了打印。我将更新我的问题以显示更接近我的代码的版本。我应该将此作为错误报告给 enum 模块吗?提前致谢
    • @RodrigoLaguna:去报告吧。我不确定它是否会被修复,因为它超出了Enum 的预期用途,并且任何“修复”都会变得脆弱。不过,我相信我们可以找到更好的解决方法(可能使用ignore)。
    • @RodrigoLaguna:最终更新:添加了一个您可能更喜欢的自定义枚举。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-08
    • 1970-01-01
    • 2022-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多