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

标签 python enums deprecation-warning

我有一个枚举,其中一些成员已被弃用:

from enum import Enum

class Foo(Enum):
    BAR = "bar"
    BAZ = "baz"  # deprecated


它如何获得以下行为:
  • 当有人写 Foo.BAR ,一切正常
  • 当有人写 Foo.BAZ , DeprecationWarning使用 warnings.warn("BAZ is deprecated", DeprecationWarning) 发布.之后一切正常。
  • 当以其他方式访问成员时,应应用相同的行为,例如Foo("baz")Foo["BAZ"]应该提出 DeprecationWarning .

  • 我尝试过但失败的事情:
  • 覆盖 _missing_并且不要定义 BAZ .不起作用,因为最后我仍然需要返回现有成员一段时间(直到我们的数据库清除了弃用的值)。
    但我不能动态地将成员添加到枚举中。如果我定义它,_missing_不叫。
  • 覆盖任何 __getattr__ , __getattribute__ .这些在访问成员的属性时被调用,例如Foo.BAZ.boo , 不是在访问 Foo.BAZ 时.如果我可以覆盖 __getattr__,我想这可以工作的 EnumMeta然后制作 Enum使用子元类。但是,我不知道如何做到这一点
  • 覆盖 __class_getitem__ : 保留用于静态类型,无论如何都不会调用。
  • 滥用 _generate_next_value_ .此函数仅在创建类时调用,因此当类被调用一次时,无论是否调用了已弃用的成员,我都会收到弃用警告。但这不是我想要的。
  • this question .它不能解决我的问题,因为目标是在迭代过程中过滤已弃用的成员。

  • TLDR:访问枚举成员时如何检测和调用函数?

    我正在使用 python 3.8,所以新功能很好。

    最佳答案

    这似乎是 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 )图书馆。

    关于python - 访问python枚举成员时如何检测和调用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62299740/

    相关文章:

    python - 如何在另一个 AWS 区域使用不同的 AWS 终端节点?

    python - 如何在Python中的数据框中索引列表元素?

    typescript - 枚举不适用于 Nestjs 和 graphql

    node.js - 如何允许 Mongoose 模式中的枚举字段为空?

    android - 在 API 级别 30 (Android 11) : getDefaultDisplay() and getMetrics() are now deprecated. 上获取屏幕宽度我们应该改用什么?

    MySQL 错误 1287 - 插入表时显示分区已弃用错误

    java - 无法获取 Apache POI 来进行 Excel 阅读

    python - 为什么我的 python if 语句不起作用?

    swift - 有没有办法让函数接受任何具有 rawValue 字符串的枚举类型?

    python - 将列表中的字符串转换为 float