要使枚举类完全“只读”,只需要使用
__setattr__
hook
防止
全部的
属性分配。因为元类附加到类
之后
它是创建的,分配正确的枚举值没有问题。
就像伊桑的回答一样,我用的是
EnumMeta
类作为自定义元类的基础:
from enum import EnumMeta, Enum
class FrozenEnumMeta(EnumMeta):
"Enum metaclass that freezes an enum entirely"
def __new__(mcls, name, bases, classdict):
classdict['__frozenenummeta_creating_class__'] = True
enum = super().__new__(mcls, name, bases, classdict)
del enum.__frozenenummeta_creating_class__
return enum
def __call__(cls, value, names=None, *, module=None, **kwargs):
if names is None: # simple value lookup
return cls.__new__(cls, value)
enum = Enum._create_(value, names, module=module, **kwargs)
enum.__class__ = type(cls)
return enum
def __setattr__(cls, name, value):
members = cls.__dict__.get('_member_map_', {})
if hasattr(cls, '__frozenenummeta_creating_class__') or name in members:
return super().__setattr__(name, value)
if hasattr(cls, name):
msg = "{!r} object attribute {!r} is read-only"
else:
msg = "{!r} object has no attribute {!r}"
raise AttributeError(msg.format(cls.__name__, name))
def __delattr__(cls, name):
members = cls.__dict__.get('_member_map_', {})
if hasattr(cls, '__frozenenummeta_creating_class__') or name in members:
return super().__delattr__(name)
if hasattr(cls, name):
msg = "{!r} object attribute {!r} is read-only"
else:
msg = "{!r} object has no attribute {!r}"
raise AttributeError(msg.format(cls.__name__, name))
class FrozenEnum(Enum, metaclass=FrozenEnumMeta):
pass
上面区分了已经可用的属性和新的属性,以便于诊断。它还阻止属性
删除
,这可能同样重要!
它还提供元类和
FrozenEnum
基类
用于枚举;使用它而不是
Enum
.
冻结样品
Color
枚举:
>>> class Color(FrozenEnum):
... red = 1
... green = 2
... blue = 3
...
>>> list(Color)
[, , ]
>>> Color.foo = 'bar'
Traceback (most recent call last):
# ...
AttributeError: 'Color' object has no attribute 'foo'
>>> Color.red = 42
Traceback (most recent call last):
# ...
Cannot reassign members.
>>> del Color.red
Traceback (most recent call last):
# ...
AttributeError: Color: cannot delete Enum member.
注意
全部的
不允许更改属性,不允许使用新属性,也阻止删除。当名称是枚举成员时,我们委托给原始成员
枚举元
处理以保持错误消息稳定。
如果枚举使用更改枚举类属性的属性,则必须将这些属性白名单,或者允许设置以单个下划线开头的名称;在
第二组
确定允许设置和使用的名称
super().__setattr__(name, value)
对于这些异常,就像代码现在通过使用标志属性区分类构造和以后的更改一样。
上面的类可以像
Enum()
要以编程方式创建枚举,请执行以下操作:
e = FrozenEnum('Things', [('foo',1), ('bar',2)]))
演示:
>>> e = FrozenEnum('Things', [('foo',1), ('bar',2)])
>>> e
>>> e.foo = 'bar'
Traceback (most recent call last):
# ...
AttributeError: Cannot reassign members.