访问python枚举成员时如何检测和调用函数 [英] How do I detect and invoke a function when a python enum member is accessed

查看:148
本文介绍了访问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

它如何获得以下行为:


  • 何时有人写 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, 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:


  • 覆盖 _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 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:如何检测和调用

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆