访问python枚举成员时如何检测和调用函数 [英] How do I detect and invoke a function when a python enum member is accessed
问题描述
我有一个不推荐使用某些成员的枚举:
I have an enum for which some of the members are deprecated:
from enum import Enum
class Foo(Enum):
BAR = "bar"
BAZ = "baz" # deprecated
它如何获得以下行为:
- 何时有人写
Foo.BAR
,一切正常 - 当有人写
Foo.BAZ
,使用warnings.warn( BAZ已弃用,DeprecationWarning)
发出DeprecationWarning
。 - 以其他方式访问成员时,应采用相同的行为。
Foo( baz)
和Foo [ BAZ]
应该引发DeprecationWarning
。
- When somebody writes
Foo.BAR
, everything behaves normally - When somebody writes
Foo.BAZ
, aDeprecationWarning
is issued usingwarnings.warn("BAZ is deprecated", DeprecationWarning)
. Afterwards everything behaves normally. - The same behavior should apply when members are accessed in other ways, e.g.
Foo("baz")
andFoo["BAZ"]
should raise aDeprecationWarning
.
我尝试过但失败的事情:
Things I have tried, but failed:
- 覆盖
_missing _
,而不定义BAZ
。这是行不通的,因为最后我仍然需要一段时间返回一个现有成员(直到我们的数据库清除了不赞成使用的值)。
但是我不能动态地将成员添加到枚举中。如果我定义它,则不会调用_missing _
。 - 会覆盖任何
__ getattr __
,__ getattribute __
。当访问成员的属性(例如,Foo.BAZ.boo
,而不是在访问Foo.BAZ
时。我想如果我可以覆盖EnumMeta
的__ getattr __
然后制作Enum 的话,这可能行得通。 code>使用子元类。但是,我也看不到怎么做
- 覆盖
__ class_getitem __
:保留用于静态类型,无论如何都不会调用。 / li>
- 滥用行为
_generate_next_value _
。仅在创建类时才调用此函数,因此,无论是否调用了不赞成使用的成员,一次调用该类时都会收到不赞成使用的警告。但这不是我想要的。 - 查看此问题。它并不能解决我的问题,因为目标是在迭代过程中过滤不赞成使用的成员。
- Overwrite
_missing_
and don't defineBAZ
. Does not work, because in the end I still need to return an existing member for a while (until our DB is cleaned of the deprecated value). But I can not dynamically add members to an enum. If I define it,_missing_
is not called. - overwrite any of
__getattr__
,__getattribute__
. These are called when accessing attributes of a member, e.g.Foo.BAZ.boo
, not when accessingFoo.BAZ
. I guess this could work if I could overwrite__getattr__
ofEnumMeta
and then makeEnum
use the child meta class. However, I don't see how that can be done either - overwrite
__class_getitem__
: Reserved for static typing and not called anyways. - Abuse
_generate_next_value_
. This function is only called on class creation, so I can get a deprecation warning when the class is called once, regardless of whether the deprecated member is called or not. But that is not what I want. - Look at this question. It does not solve my problem, as the goal there is filtering of deprecated members during iteration.
TLDR:如何检测和调用
TLDR: How can I detect and invoke a function when an enum member is accessed?
我正在使用python 3.8,所以新功能很好。
I am working with python 3.8, so new features are fine.
推荐答案
这似乎是将 EnumMeta
是正确的选择。
This appears to be one of those times when subclassing EnumMeta
is the right thing to do.
新的元类将运行 _on_access
方法(如果存在),只要有成员被访问:
The new metaclass will run an _on_access
method, if it exists, whenever a member is accessed:
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
对待成员的任何其他参数创建作为弃用
函数的参数,并且仅在给出附加参数的情况下,将 _on_access
属性设置为该函数:
The new base Enum
treats any extra arguments on member creation as arguments for a deprecate
function, and sets the _on_access
attribute to that function only if extra arguments are given:
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
中已弃用成员:
And our example Enum
with deprecated members:
class Foo(DeprecatedEnum):
BAR = "bar"
BAZ = "baz", "use something else"
和警告(来自测试脚本):
And the warnings (from a test script):
# 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
中所有不赞成使用的成员:
And all the deprecated members in Foo
:
>>> print([m.name for m in Foo if m._args])
['BAZ']
披露:我是 Python stdlib 枚举
, enum34
向后移植,并且高级枚举( aenum
)库。
Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
这篇关于访问python枚举成员时如何检测和调用函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!