python - 为 Enum 的子类重载 __init__()

标签 python python-3.x inheritance enums python-3.6

我正在尝试重载枚举子类的 __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

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

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

    def __init__(self, value):
        """Accepts int or date"""
        if isinstance(value, date):
            super().__init__(date.weekday())
        else:
            super().__init__(value)


assert WeekDay(0) == WeekDay.MONDAY
assert WeekDay(date(2019, 4, 3)) == WeekDay.MONDAY
# ---------------------------------------------------------------------------
# TypeError                                 Traceback (most recent call last)
# /path/to/my/test/file.py in <module>()
#      27 
#      28 
# ---> 29 class WeekDay(Enum):
#      30     MONDAY = 0
#      31     TUESDAY = 1

# /path/to/my/virtualenv/lib/python3.6/enum.py in __new__(metacls, cls, bases, classdict)
#     208             enum_member._name_ = member_name
#     209             enum_member.__objclass__ = enum_class
# --> 210             enum_member.__init__(*args)
#     211             # If another member with the same value was already defined, the
#     212             # new member becomes an alias to the existing one.

# /path/to/my/test/file.py in __init__(self, value)
#      40             super().__init__(date.weekday())
#      41         else:
# ---> 42             super().__init__(value)
#      43 
#      44 

# TypeError: object.__init__() takes no parameters

最佳答案

您必须重载 _missing_ Hook 。 WeekDay 的所有实例都是在首次定义该类时创建的; WeekDay(date(...)) 是索引操作而不是创建操作,__new__ 最初是寻找绑定(bind)到整数 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)

几个例子:

>>> WeekDay(date(2019,3,7))
<WeekDay.THURSDAY: 3>
>>> assert WeekDay(date(2019, 4, 1)) == WeekDay.MONDAY
>>> assert WeekDay(date(2019, 4, 3)) == WeekDay.MONDAY
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

(注意:_missing_ 在 Python 3.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

关于python - 为 Enum 的子类重载 __init__(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55051654/

相关文章:

python - 如何在 Windows 上重新训练图像

python - 如何将工作表转换为 Pandas 中的数据框?

python - 在 Tkinter 中集成 click-Terminal?

java - Java 中的接口(interface)继承

c# - 从基类更改属性 setter 行为

python - 如何在 python 中重命名父类(super class)的方法?

python - 为 Jupyter Notebook 安装 ffmpeg

Python + Tkinter Windows 7 任务栏进度

python - 文本文档中包含最多字符实例的行

c++ - C++中公共(public)继承基类调用派生类的私有(private)虚函数