python - 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
解决方案
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.
推荐阅读
- jquery - 尝试为正则表达式模式(数字、文件类型、密码等)做 jquery 验证方法
- c++ - 在 Mac OS Catalina 中设置 --std=C++17 默认值
- android - 如何使用改造向 api 添加身份验证?
- angular - 如何从 takeWhile 条件发出效果错误?
- python - 为什么我尝试使用 Requests 和 Beautiful Soup 4 抓取的 URL 返回 TypeError?
- perl - Perl:在 perl 中比较两个文件
- authentication - 我想从 webscrape traceparts.com 下载他们的所有 Solidworks 文件
- java - 如何从外部目录中的 jar 创建 bean
- c++ - 在 Visual Studio Code 中调试(C++ 程序)时看不到矢量或其他容器(例如地图)的内容
- python - 如何使用python查看由字节字符串表示的两个jpg图像是否相同