为Enum的子类重载\uUu init_Uu()

2024-09-23 22:23:30 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图重载枚举子类的__init__()方法。奇怪的是,普通类的模式不再适用于Enum。在

下面显示了使用普通类的所需模式:

class Integer:
    def __init__(self, a):
        """Accepts only int"""
        assert isinstance(a, int)
        self.a = a

    def __repr__(self):
        return str(self.a)


class RobustInteger(Integer):
    def __init__(self, a):
        """Accepts int or str"""
        if isinstance(a, str):
            super().__init__(int(a))
        else:
            super().__init__(a)


print(Integer(1))
# 1
print(RobustInteger(1))
# 1
print(RobustInteger('1'))
# 1

如果与枚举一起使用,则相同的模式将中断:

^{pr2}$

Tags: 方法selfinitdef模式integerclassaccepts
2条回答

有一个更好的答案,但我还是把它贴出来,因为它可能有助于理解这个问题。

文件给出了以下提示:

EnumMeta creates them all while it is creating the Enum class itself, and then puts a custom new() in place to ensure that no new ones are ever instantiated by returning only the existing member instances.

所以我们必须等待重新定义__new__,直到类被创建。通过一些难看的修补,这通过了测试:

from enum import Enum
from datetime import date

class WeekDay(Enum):
    MONDAY = 0 
    TUESDAY = 1 
    WEDNESDAY = 2 
    THURSDAY = 3 
    FRIDAY = 4 
    SATURDAY = 5 
    SUNDAY = 6 

wnew = WeekDay.__new__

def _new(cls, value):
    if isinstance(value, date):
        return wnew(cls, value.weekday()) # not date.weekday()
    else:
        return wnew(cls, value)

WeekDay.__new__ = _new

assert WeekDay(0) == WeekDay.MONDAY
assert WeekDay(date(2019, 3, 4)) == WeekDay.MONDAY # not 2019,4,3

必须重载_missing_钩子。WeekDay的所有实例都是在首次定义类时创建的;WeekDay(date(...))是索引操作,而不是创建操作,__new__最初查找绑定到整数0到6的预先存在的值。否则,它将调用_missing_,在这种情况下,您可以将date对象转换为这样一个整数。在

class WeekDay(Enum):
    MONDAY = 0
    TUESDAY = 1
    WEDNESDAY = 2
    THURSDAY = 3
    FRIDAY = 4
    SATURDAY = 5
    SUNDAY = 6

    @classmethod
    def _missing_(cls, value):
        if isinstance(value, date):
            return cls(value.weekday())
        return super()._missing_(value)

几个例子:

^{pr2}$

(注意:_missing_在python3.6之前不可用。)


在3.6之前,您似乎可以重写EnumMeta.__call__进行相同的检查,但我不确定这是否会产生意外的副作用。(关于__call__的推理总是让我头晕目眩。)

# Silently convert an instance of datatime.date to a day-of-week
# integer for lookup.
class WeekDayMeta(EnumMeta):
    def __call__(cls, value, *args, **kwargs):
        if isinstance(value, date):
            value = value.weekday())
        return super().__call__(value, *args, **kwargs)

class WeekDay(Enum, metaclass=WeekDayMeta):
    MONDAY = 0
    TUESDAY = 1
    WEDNESDAY = 2
    THURSDAY = 3
    FRIDAY = 4
    SATURDAY = 5
    SUNDAY = 6

相关问题 更多 >