python 函数参数枚举,访问python枚举成员时如何检测和调用函数

I have an enum for which some of the members are deprecated:

from enum import Enum

class Foo(Enum):

BAR = "bar"

BAZ = "baz" # deprecated

How do it get the following behavior:

When somebody writes Foo.BAR, everything behaves normally

When somebody writes Foo.BAZ, a DeprecationWarning is issued using warnings.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") and Foo["BAZ"] should raise a DeprecationWarning.

Things I have tried, but failed:

Overwrite _missing_ and don't define BAZ. 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 accessing Foo.BAZ. I guess this could work if I could overwrite __getattr__ of EnumMeta and then make Enum 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: How can I detect and invoke a function when an enum member is accessed?

I am working with python 3.8, so new features are fine.

解决方案

This appears to be one of those times when subclassing EnumMeta is the right thing to do.

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

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,

)

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']

And all the deprecated members in Foo:

>>> print([m.name for m in Foo if m._args])

['BAZ']

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值