gpt4 book ai didi

Python Enum 防止无效的属性赋值

转载 作者:行者123 更新时间:2023-11-28 21:33:47 24 4
gpt4 key购买 nike

当我使用 Functional API 创建一个 Enum 时,我得到一个允许任意赋值的 Enum 对象(即它有一个 __dict__):

e = enum.Enum('Things',[('foo',1),('bar',2)])
e.baz = 3

该项目没有出现在列表中:

list(e)
[<foo.foo: 1>, <foo.bar: 2>]

但还是可以引用:

if thing == e.baz: ...

虽然这似乎不太可能发生,但我想使用枚举的原因之一是防止拼写错误和字符串文字,并在导入模块时或尽可能早地捕获这些东西。

有没有办法动态构建一个行为更像 __slots__ 对象且不允许分配任意属性的 Enum?

最佳答案

要使枚举类完全“只读”,所需要的只是一个使用 __setattr__ hook 的元类。这会阻止所有 属性分配。因为元类在创建后附加到类,所以分配正确的枚举值没有问题。

像 Ethan 的回答一样,我使用 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.red: 1>, <Color.green: 2>, <Color.blue: 3>]
>>> 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.

请注意,所有 属性更改都是不允许的,不允许新属性,删除也被阻止。当名称是枚举成员时,我们委托(delegate)给原始的 EnumMeta 处理以保持错误消息的稳定性。

如果您的枚举使用改变枚举类属性的属性,您要么必须将它们列入白名单,要么允许设置以单个下划线开头的名称;在 __setattr__ 中确定允许设置哪些名称并使用 super().__setattr__(name, value) 来处理这些异常,就像代码现在区分类构造和以后使用标志属性进行更改。

可以像 Enum() 一样使用上面的类以编程方式创建枚举:

e = FrozenEnum('Things', [('foo',1), ('bar',2)]))

演示:

>>> e = FrozenEnum('Things', [('foo',1), ('bar',2)])
>>> e
<enum 'Things'>
>>> e.foo = 'bar'
Traceback (most recent call last):
# ...
AttributeError: Cannot reassign members.

关于Python Enum 防止无效的属性赋值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54274002/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com