具有可变数量属性的枚举成员的 Python 元类

Posted

技术标签:

【中文标题】具有可变数量属性的枚举成员的 Python 元类【英文标题】:Python metaclass for Enum members with variable number of attributes 【发布时间】:2021-08-22 18:49:04 【问题描述】:

我是 Python 元类的新手,目前正在尝试实现以下目标:

from enum import Enum, EnumMeta

class FlexEnumMeta(EnumMeta):
   # see the attempt for the implementation below
   ...

class FlexEnum(Enum, metaclass=FlexEnumMeta, attrs=('foo', 'bar')):
   X = 'abc', 123
   Y = 'def', 456

我希望它可以按如下方式工作:

FlexEnum.X.foo
>>> 'abc'

我阅读了 Enum 和 aenum 的部分源代码以试图理解,据我所知,这是 FlexEnumMeta 的代码:

class FlexEnumMeta(EnumMeta):

    @classmethod
    def __prepare__(metacls, cls, bases, attrs=None, **kwargs):
        return 

    def __new__(metacls, name, bases, clsdict, attrs=None, **kwargs):
        # Proper way to get an _EnumDict,
        # to be passed to super().__new__
        enum_dict = super().__prepare__(name, bases, **kwargs)

        # Enumeration class members
        members = []

        for member_name, values in clsdict.items():
            # Copy original clsdict in final class members list
            if member_name.startswith('_'):
                enum_dict[member_name] = values
                continue

            # Create correspondance between attributes names and values
            value_dict = dict(zip(attrs, values))
            members.append((member_name, value_dict))

        # Copy custom class members in final class members list
        for key, value in members:
            enum_dict[key] = value

        return super().__new__(metacls, name, bases, enum_dict)

class FlexEnum(Enum, metaclass=FlexEnumMeta, attrs=('foo', 'bar')):
    X = 'abc', 123
FlexEnum.X
>>> <FlexEnum.X: 'foo': 'abc', 'bar': 123>
FlexEnum.X.value['foo']
>>> 'abc'

我尝试使用 aenum 的 MultiValue 设置,但我需要将 dict 作为我的属性之一,并且这些属性不可散列。 最后,这对我来说是一种锻炼。

【问题讨论】:

每个成员的属性和值是相同的还是分开的?如果分开,实际值重要吗?是否需要通过foobar 的值来查找成员? 所有成员的属性相同,但值不同。我实际上不需要查找除了姓名以外的任何其他成员,因此 chepner 的答案可能就是我想要的。我想我真的想在不合适也没有必要的地方使用元类。 【参考方案1】:

您只需要定义FlexEnum.__init__

import enum


class FlexEnum(enum.Enum):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    X = 'abc', 123
    Y = 'def', 456

然后

>>> FlexEnum.X.foo
'abc'

成员本身的值将是元组。如果你想要别的东西,你可以覆盖__new__。文档中有更多信息,When to use __new__() vs. __init__()

【讨论】:

感谢您的旅游回答,这似乎不合适。尽管如此,是否有办法在元类级别封装这种初始化,例如从这个元类派生的所有类都可以使用其成员的属性作为参数进行初始化? FlexEnum.X.foo 的示例repr 错误,即'abc' 属性,FlexEnum.X.bar123 @Blckknght 这是示例输出中的错字。类定义本身是正确的。

以上是关于具有可变数量属性的枚举成员的 Python 元类的主要内容,如果未能解决你的问题,请参考以下文章

Python枚举的定义和使用(enum)

1104课堂小结

iOS中类元类isa详解

iOS-isa指针;objc实例对象类元类根类根元类

python 的元类

python 元类