这似乎是 subclassing EnumMeta 是正确的做法之一。
新的元类将运行_on_access 方法(如果存在),每当访问成员时:
class OnAccess(EnumMeta):
"""
runs a user-specified function whenever member is accessed
"""
#
def __getattribute__(cls, name):
obj = super().__getattribute__(name)
if isinstance(obj, Enum) and obj._on_access:
obj._on_access()
return obj
#
def __getitem__(cls, name):
member = super().__getitem__(name)
if member._on_access:
member._on_access()
return member
#
def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
obj = super().__call__(value, names, module=module, qualname=qualname, type=type, start=start)
if isinstance(obj, Enum) and obj._on_access:
obj._on_access()
return obj
新的基础Enum 将创建成员时的任何额外参数视为deprecate 函数的参数,并且仅在提供额外参数时才将_on_access 属性设置为该函数:
class DeprecatedEnum(Enum, metaclass=OnAccess):
#
def __new__(cls, value, *args):
member = object.__new__(cls)
member._value_ = value
member._args = args
member._on_access = member.deprecate if args else None
return member
#
def deprecate(self):
args = (self.name, ) + self._args
import warnings
warnings.warn(
"member %r is deprecated; %s" % args,
DeprecationWarning,
stacklevel=3,
)
我们的示例 Enum 带有已弃用的成员:
class Foo(DeprecatedEnum):
BAR = "bar"
BAZ = "baz", "use something else"
以及警告(来自测试脚本):
# no warning here
list(Foo)
# nor for non-deprecated members
Foo.BAR
# but direct use of deprecated members does generate warnings
Foo.BAZ
/home/ethan/test:74: DeprecationWarning: member 'BAZ' is deprecated; use something else
Foo.BAZ
Foo('baz')
/home/ethan/test:75: DeprecationWarning: member 'BAZ' is deprecated; use something else
Foo('baz')
Foo['BAZ']
/home/ethan/test:76: DeprecationWarning: member 'BAZ' is deprecated; use something else
Foo['BAZ']
以及Foo 中所有已弃用的成员:
>>> print([m.name for m in Foo if m._args])
['BAZ']
披露:我是 Python stdlib Enum、enum34 backport 和 Advanced Enumeration (aenum) 库的作者。