首页 > 解决方案 > 带有强制关键字参数的自定义类的 Python 枚举

问题描述

我正在尝试使用具有强制性位置参数的自定义类创建一个枚举。

class Coord:
  def __init__(self, *, x, y):
    self._x = x
    self._y = y

我想用这个类做几个枚举。像:

class IntCoord(Coord, Enum):
  ONE_ONE = Coord(x=1, y=1)

class FloatCoord(Coord, Enum):
  ONE_ONE = Coord(x=0.1, y=0.1)

这样我就可以在其他类中使用它们,例如:

class System(Generic[T]):
  @classmethod
  def get_one_one(cls) -> T:
    return NotImplemented

class IntSystem(System[IntCoord]):
  @classmethod
  def get_one_one(cls) -> IntCoord:
    return IntCoord.ONE_ONE

class FloatSystem(System[FloatCoord]):
  @classmethod
  def get_one_one(cls) -> FloatCoord:
    return FloatCoord.ONE_ONE

之后,我想比较枚举:

IntCoord(x=1, y=1) is IntSystem.get_one_one()

我很难定义Coord班级。似乎不可能通过以下方式实例化Coord枚举类Coord(x=1, y=1)

有没有用枚举实现这一目标的好方法?还是更好的合理实施?

完整脚本如下:

from enum import Enum
from typing import Generic, TypeVar


class Coord:
    def __init__(self, *, x, y):
        self._x = x
        self._y = y


class IntCoord(Coord, Enum):
    ONE_ONE = Coord(x=1, y=1)


class FloatCoord(Coord, Enum):
    ONE_ONE = Coord(x=0.1, y=0.1)


T = TypeVar('T', bound=Coord)


class System(Generic[T]):
    @classmethod
    def get_one_one(cls) -> T:
        return NotImplemented


class IntSystem(System[IntCoord]):
    @classmethod
    def get_one_one(cls) -> IntCoord:
        return IntCoord.ONE_ONE


class FloatSystem(System[FloatCoord]):
    @classmethod
    def get_one_one(cls) -> FloatCoord:
        return FloatCoord.ONE_ONE


if __name__ == '__main__':
    int_one_one = IntSystem.get_one_one()
    float_one_one = FloatSystem.get_one_one()

    print(IntCoord(x=1, y=1) is int_one_one)
    print(FloatCoord(x=0.1, y=0.1) is float_one_one)

错误是:

Traceback (most recent call last):
  File "test.py", line 11, in <module>
    class IntCoord(Coord, Enum):
  File ".pyenv/versions/3.9.0/lib/python3.9/enum.py", line 228, in __new__
    enum_member.__init__(*args)
TypeError: __init__() takes 1 positional argument but 2 were given

标签: pythonenums

解决方案


你遇到的问题CoordEnum你说你的 enum 也是 a Coord,并且 aCoord需要两个参数——但你只给你的 enum 成员ONE_ONE一个参数...... a Coord

有两种方法可以解决这个问题:

  • 不要Coord在您的枚举成员中使用 a (并丢失您的关键字(不是位置)参数):
    class Coord:
        def __init__(self, x, y):
            self._x = x
            self._y = y

    class IntCoord(Coord, Enum):

        ONE_ONE = 1, 1
  • 不要Coord在您的枚举标头中使用(并保留您的关键字参数,但添加sproperty以便仍然有效]:blah._xblah._y
    class Coord:
        def __init__(self, *, x, y):
            self._x = x
            self._y = y

    class IntCoord(Enum):
        #
        ONE_ONE = Coord(x=1, y=1)
        #
        @property
        def _x(self):
            return self.value._x
        #
        @property
        def _y(self):
            return self.value._y

如果您改用aenum1库,它确实会变得更容易:

from aenum import Enum, enum

class Coord:
    def __init__(self, *, x, y):
        self._x = x
        self._y = y

class IntCoord(Coord, Enum):
    #
    ONE_ONE = enum(x=1, y=1)

并在使用中:

>>> IntCoord.ONE_ONE
<IntCoord.ONE_ONE: enum(x=1, y=1)>

>>> IntCoord.ONE_ONE._y
1

请注意,在上述所有情况下,IntCoord.ONE_ONE按值检索比平时要复杂一些:

  1. IntCoord((1, 1))
  2. IntCoord(Coord(x=1, y=1))
  3. IntCoord(enum(x=1, y=1))

--

1披露:我是Python stdlibEnumenum34backportAdvanced Enumeration ( aenum) 库的作者。


推荐阅读