自定义 __repr__ 作为从 Enum 派生的类的类方法

Posted

技术标签:

【中文标题】自定义 __repr__ 作为从 Enum 派生的类的类方法【英文标题】:custom __repr__ as a class method for a class derived from Enum 【发布时间】:2021-06-17 18:56:31 【问题描述】:

我有一个派生自 enum.Enum 的类。 现在 enum.Enum 中的 repr 指的是 enum.Enum 的成员,而不是整个类。

举例

from enum import Enum
class endpoints(Enum):
""" shop """
    shop = 'shop'
    countries = 'countries'
    policies = 'policies'
    
print(endpoints)

结果

<enum 'endpoints'>

我尝试了什么

from enum import Enum
class endpoints(Enum):
    shop = 'shop'
    countries = 'countries'
    policies = 'policies/object_id'
    @classmethod
    def __repr__(cls):
        return  '\n'.join([str(member.name) + ':' + (member.value) for member in cls.__members__])

print(endpoint)

        

结果

<enum 'endpoints'>

这表明 repr 实际上是 endpoints.member。repr 我需要的是端点。repr

我知道这可以通过元类here 来实现,但由于 Enum 本身有一个元类,我不能从另一个元类继承/分配。

没有修改 enum.Enum 我该如何实现我的目标。

想要的输出如下。

print(endpoints)
shop : shop
countries : countries 
policies : policies/object_id

【问题讨论】:

您的minimal reproducible example 有语法错误。无法复制。 @wwii 修复了错误 【参考方案1】:

要修改类对象的打印方式,就像任何对象一样,您需要修改它的类__repr__,即元类__repr__,而不是简单地用MyClass.__repr__ 装饰classmethod。在这种情况下,您需要自定义EnumMeta

In [1]: from enum import EnumMeta, Enum

In [2]: class CustomEnumMeta(EnumMeta):
   ...:     def __repr__(self):
   ...:         return '\n'.join([f"name: value" for name, value in self.__members__.items()])
   ...:

In [3]: class Endpoints(Enum, metaclass=CustomEnumMeta):
   ...:     shop = 'shop'
   ...:     countries = 'countries'
   ...:     policies = 'policies'
   ...:

In [4]: Endpoints
Out[4]:
shop: Endpoints.shop
countries: Endpoints.countries
policies: Endpoints.policies

基本上,

但由于 Enum 本身有一个元类,我不能从另一个元类继承/分配。

错了。您可以提供自定义元类如果它派生自基类的元类就好了,即使您的基类有元类。它必须以这种方式工作,因为所有类都有一个元类,默认情况下,type

来自documentation:

3.3.3.3。确定合适的元类

如果没有给出基数和显式元类,则使用type()

如果给出了一个显式的元类并且它不是type()的实例,那么它直接作为元类使用;

如果type() 的实例作为显式元类给出,或者定义了基类,则使用最派生的元类。

从显式指定的元类(如果有)和所有的元类(即type(cls))中选择派生最多的元类 指定的基类。派生最多的元类是一个 所有这些候选元类的子类型。

添加了重点。所以在这个简单的单继承案例中,由于CustomEnumMetaCustomEnumMetaEnumMeta之间派生最多的元类,那么它就被用作元类。

【讨论】:

这是我一直在寻找的答案。你是对的就是说我错了。当我尝试使用自定义元类的解决方案时,我收到一个语法错误,说明我的元类和枚举元类之间存在冲突,因为我的元类不完整并且无法创建对象,从原始元类派生是一个更明智的解决方案。 【参考方案2】:

__repr__ 仅在类实例上调用,例如 endpoints('shop'),而不是在类本身上。

您可以使用自己的类方法(如enum_members(cls))代替__repr__,然后您可以在类本身而不是实例上调用这个自己的方法。

您还必须使用print(repr(obj)) 而不仅仅是print(obj) 才能使用__repr__ 方法。如果您只想print(obj),则创建__str__ 方法而不是__repr__

使用list(cls) 代替cls.__members__ 枚举所有枚举成员。

Try it online!

from enum import Enum
class endpoints(Enum):
    shop = 'shop'
    countries = 'countries'
    policies = 'policies/object_id'
    @classmethod
    def __repr__(cls):
        return '\n'.join([str(member.name) + ':' + (member.value) for member in list(cls)])

print(repr(endpoints('shop')))

输出:

shop:shop
countries:countries
policies:policies/object_id

【讨论】:

以上是关于自定义 __repr__ 作为从 Enum 派生的类的类方法的主要内容,如果未能解决你的问题,请参考以下文章

自定义输出内容 __str__() 和 __repr__() 以及__format__() 的使用

如何直接使用 `str.format` 作为 `__repr__`?

__str__和__repr__的区别

Python进阶-----通过类的内置方法__str__和__repr__自定制输出(打印)对象时的字符串信息

Python中的__str__()方法和__repr__()方法

尚硅谷_Java零基础教程(常用类——枚举类Enum)-- 学习笔记