英文:
Customize "return by value" in IntEnum
问题
我有一个扩展IntEnum
类的类,它定义了位编码变量中的位置:
from enum import IntEnum
class Bits(IntEnum):
@classmethod
def data(cls, value: int):
return [ e for e in cls if (1 << e) & value ]
class Status(Bits):
READY = 0
ERROR = 1
WARNING = 2
x = Status.data(3) # x <- [<Status.READY: 0>, <Status.ERROR: 1>]
y = Status.data(4) # y <- [<Status.WARNING: 2>]
z = Status.data(8) # z <- []
能否定制IntEnum
的“按值返回”而不破坏任何东西呢?因为将有多个类扩展Bits
类,理想情况下,这个功能应该在Bits
类中实现。
我想要实现的是这样的:
x = Status(3) # x <- [<Status.READY: 0>, <Status.ERROR: 1>]
y = Status(4) # y <- [<Status.WARNING: 2>]
z = Status(8) # z <- []
我尝试重写__call__
方法(请参见enum — Support for enumerations),但在这种情况下似乎没有调用该方法。
英文:
I have a class that extends IntEnum
class that defines positions in a bit-encoded variable:
from enum import IntEnum
class Bits(IntEnum):
@classmethod
def data(cls, value: int):
return [ e for e in cls if (1 << e) & value ]
class Status(Bits):
READY = 0
ERROR = 1
WARNING = 2
x = Status.data(3) # x <- [<Status.READY: 0>, <Status.ERROR: 1>]
y = Status.data(4) # y <- [<Status.WARNING: 2>]
z = Status.data(8) # z <- []
Would it be possible to customize "return by value" of IntEnum
without breaking anything? Since there will be multiple classes that will extend the Bits
class, ideally this functionality should be implemented in the Bits
class.
What I want to achieve is this:
x = Status(3) # x <- [<Status.READY: 0>, <Status.ERROR: 1>]
y = Status(4) # y <- [<Status.WARNING: 2>]
z = Status(8) # z <- []
I tried overriding the __call__
method (see enum — Support for enumerations), but it does not seem to be called in this case.
答案1
得分: 2
是的,您可以通过为Status
类定义自定义的__new__
方法来定制IntEnum
类的返回值。这样可以改变新实例的创建方式,而不会破坏IntEnum的功能。
英文:
Yes you could customize the return value of an IntEnum
class by defining a custom __new__
method for your Status
class, this way it will change the behavior of how new instances are created without breaking the functionality of the IntEnum.
from enum import IntEnum, EnumMeta
class BitsMeta(EnumMeta):
def __call__(cls, value):
if isinstance(value, int):
return [e for e in cls if (1 << e.value) & value]
return super().__call__(value)
class Bits(IntEnum, metaclass=BitsMeta):
@classmethod
def data(cls, value: int):
return [e for e in cls if (1 << e.value) & value]
class Status(Bits):
READY = 0
ERROR = 1
WARNING = 2
x = Status(3) # x <- [<Status.READY: 0>, <Status.ERROR: 1>]
y = Status(4) # y <- [<Status.WARNING: 2>]
z = Status(8) # z <- []
print(x)
print(y)
print(z)
答案2
得分: 1
根据你需要如何使用 x
,y
和 z
,IntFlag
可能适合你:
class Status(IntFlag):
READY = 0
ERROR = 1
WARNING = 2
x = Status(3)
repr(x)
# <Status.ERROR|WARNING: 3>
Status.Error in x
# True
list(x)
# [<Status.Error: 1>, <Status.Warning: 2>]
这似乎更正确,因为 Status.READY
不应该是 Status(3)
的一部分,而 Status.WARNING
应该是。
更仔细看了一下,我看到你的值实际上是位位置 - 因此,虽然 Status.READY
的 value
属性是 0
,但如果传入 1
,它就被视为设置... 我可以理解这可能会令人困惑。
这是我会怎么做的:
class Status(IntFlag, boundary=CONFORM):
READY = auto()
ERROR = auto()
WARNING = auto()
x = Status(3)
repr(x)
# <Status.READY|ERROR: 3>
CONFORM
边界意味着超出范围的值会被忽略(就像你的原文中的 8
)。
现在,我的解决方案和你的解决方案之间的区别在于单个成员显示的值:
我的
<Status.READY: 1>
你的
<Status.READY: 0>
你需要决定哪个版本在你的环境中更有意义。
披露:我是 Python 标准库中的 Enum
作者, enum34
回溯 和 高级枚举(aenum
) 库的作者。
英文:
Depending on how you need to use x
, y
, and z
, IntFlag
may work for you:
class Status(IntFlag):
READY = 0
ERROR = 1
WARNING = 2
x = Status(3)
repr(x)
# <Status.ERROR|WARNING: 3>
Status.Error in x
# True
list(x)
# [<Status.Error: 1>, <Status.Warning: 2>]
This also seems more correct, since Status.READY
shouldn't be part of Status(3)
, and Status.WARNING
should be.
Looking closer, I see your values are actually bit positions -- so while the value
attribute of Status.READY
is 0
, it's considered set if 1
is passed in... I can see that being confusing.
Here's how I would do it:
class Status(IntFlag, boundary=CONFORM):
READY = auto()
ERROR = auto()
WARNING = auto()
x = Status(3)
repr(x)
# <Status.READY|ERROR: 3>
The CONFORM
boundary means out-of-range values are ignored (like 8
in your OP).
The difference now between my solution and yours is the value shown for a single member:
# mine
<Status.READY: 1>
# yours
<Status.READY: 0>
You'll have to decide which version makes more sense in your environment.
Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论