首页 > 解决方案 > NameError when using super() in enum constructor

问题描述

I am using Python 2.7.10 with the enum34 library. I am trying to do the following:

from enum import Enum


class Foo(Enum):
    def __init__(self):
        pass


class Bar(Foo):
    VALUE = 1

    def __init__(self, value):
        super(Bar, self).__init__()

When this code is run, I receive the error NameError: global name 'Bar' is not defined. Could someone help explain why I receive this error and if it's possible to call the parent constructor of an enum subclass? Thank you in advance!

Edit: Traceback (with path names redacted) for Olivier Melançon:

Traceback (most recent call last):
  File "/.../test.py", line 9, in <module>
    class Bar(Foo):
  File "/.../lib/python2.7/site-packages/enum/__init__.py", line 236, in __new__
    enum_member.__init__(*args)
  File "/.../test.py", line 13, in __init__
    super(Bar, self).__init__()
NameError: global name 'Bar' is not defined

Process finished with exit code 1

标签: pythonpython-2.7enumssupernameerror

解决方案


The problem is that Bar.VALUE.__init__ is being called before Bar exists.

You can see where this happens in EnumMeta.__new__, but even without looking at the code, it pretty much has to work this way: the whole point of an Enum class is that its enumeration members are constant values that act as attributes of the class while also being instances of it.

In that, this code will produce the exact same error in 3.4+ with the stdlib enum module, and similar errors with multiple third-party enum replacements.

In general, if you have Enum hierarchies at all, you're expected to put values only in the "leaf" classes and behavior only in the non-leaf classes. However, the only restriction that's actually explicitly documented in Restricted subclassing of enumerations is no values in non-leaf classes, so technically what you're trying to do should be legal, even if it's unusual and was never explicitly intended to work.


If you were using Python 3, there's a pretty easy workaround: just using super() instead of super(Bar, self), and it doesn't matter than Bar doesn't exist yet.

In Python 2, since that isn't doable, you need to manually simulate super. For full generality, that means writing the code to walk the mro and so on, but since multiple inheritance including two or more Enum classes isn't going to work anyway, you'll be fine just hardcoding it statically:

def __init__(self, value):
    Foo.__init__(self)

Alternatively, if you change your design to put all the behavior in non-leaf classes, that will also work:

class Foo(Enum):
    def __init__(self):
        pass

class Bar(Foo):
    def __init__(self, value):
        super(Bar, self).__init__()

class Baz(Bar):
    VALUE = 1

Most likely, whatever you're actually trying to accomplish could be done in a better way without requiring either of these changes. But since your toy example doesn't accomplishes anything, there's nothing more to show.


推荐阅读