Sek*_*raz 14 python enums python-3.x python-3.6
我希望在我的python Flags枚举中有一个"ALL"标志
myenum.EVERY_MEMBER & myenum.ALL == myenum.EVERY_MEMBER
Run Code Online (Sandbox Code Playgroud)
是的.我目前有:
from enum import Flag, auto
class RefreshFlags(Flag):
NONE = 0
EVENTS = auto()
RESOURCES = auto()
BUILDINGS = auto()
DEFENSES = auto()
.....
Run Code Online (Sandbox Code Playgroud)
因为这个枚举可能会在任何开发状态下增长,我希望有类似的东西
@property
def ALL(self):
retval = self.NONE
for member in self.__members__.values():
retval |= member
return retval
Run Code Online (Sandbox Code Playgroud)
这不起作用:
RefreshFlags.EVENTS & RefreshFlags.ALL
TypeError: unsupported operand type(s) for &: 'RefreshFlags' and 'property'
Run Code Online (Sandbox Code Playgroud)
请注意,此问题目前仅与python 3.6或更高版本有关.
有几种方法可以解决这个问题:
用一个classproperty(见 Zero's answer)
使用类装饰器(参见参考资料MSeifert's answer)
使用mixin(currently buggy)
创建一个新的基类(见下文)
使用类属性方法需要注意的一件事是,由于描述符是在类而不是元类上定义的,因此缺少对设置和删除的常规保护 - 换句话说:
>>> RefreshFlags.ALL
<RefreshFlags.DEFENSES|BUILDINGS|RESOURCES|EVENTS: 15>
>>> RefreshFlags.ALL = 'oops'
>>> RefreshFlags.ALL
'oops'
Run Code Online (Sandbox Code Playgroud)
创建一个新的基类:
# lightly tested
from enum import Flag, auto
from operator import or_ as _or_
from functools import reduce
class AllFlag(Flag):
@classproperty
def ALL(cls):
cls_name = cls.__name__
if not len(cls):
raise AttributeError('empty %s does not have an ALL value' % cls_name)
value = cls(reduce(_or_, cls))
cls._member_map_['ALL'] = value
return value
Run Code Online (Sandbox Code Playgroud)
在使用中:
class RefreshFlag(AllFlag):
EVENTS = auto()
RESOURCES = auto()
BUILDINGS = auto()
DEFENSES = auto()
>>> RefreshFlag.ALL
<RefreshFlag.DEFENSES|BUILDINGS|RESOURCES|EVENTS: 15>
Run Code Online (Sandbox Code Playgroud)
该ALL属性的有趣区别在于名称的设置_member_map_- 这允许为Enum成员提供相同的保护:
>>> RefreshFlag.ALL = 9
Traceback (most recent call last):
....
AttributeError: Cannot reassign members.
Run Code Online (Sandbox Code Playgroud)
但是,这里存在竞争条件:如果在第一次激活之前RefreshFlag.ALL = ...发生, RefreshFlag.ALL那么它就会被破坏; 出于这个原因,我会在这个实例中使用装饰器,因为装饰器会在它被破坏之前处理Enum.
# lightly tested
from enum import Flag, auto
from operator import or_ as _or_
from functools import reduce
def with_limits(enumeration):
"add NONE and ALL psuedo-members to enumeration"
none_mbr = enumeration(0)
all_mbr = enumeration(reduce(_or_, enumeration))
enumeration._member_map_['NONE'] = none_mbr
enumeration._member_map_['ALL'] = all_mbr
return enumeration
Run Code Online (Sandbox Code Playgroud)
在使用中:
@with_limits
class RefreshFlag(Flag):
EVENTS = auto()
RESOURCES = auto()
BUILDINGS = auto()
DEFENSES = auto()
>>> RefreshFlag.ALL = 99
Traceback (most recent call last):
...
AttributeError: Cannot reassign members.
>>> RefreshFlag.ALL
<RefreshFlag.DEFENSES|BUILDINGS|RESOURCES|EVENTS: 15>
>>> RefreshFlag.NONE
<RefreshFlag.0: 0>
Run Code Online (Sandbox Code Playgroud)
TL;DR 因为属性仅在类的实例上评估,而__members__只能在类上访问。
如果您访问一个property类,它只会返回一个property:
>>> RefreshFlags.ALL
<property at 0x2a5d93382c8>
Run Code Online (Sandbox Code Playgroud)
但是,要使其工作,您需要将其设为类方法:
from enum import Flag, auto
class RefreshFlags(Flag):
NONE = 0
EVENTS = auto()
RESOURCES = auto()
BUILDINGS = auto()
DEFENSES = auto()
@classmethod
def ALL(cls):
retval = self.NONE
for member in cls.__members__.values():
retval |= member
return retval
>>> RefreshFlags.ALL()
<RefreshFlags.DEFENSES|BUILDINGS|RESOURCES|EVENTS: 15>
Run Code Online (Sandbox Code Playgroud)
或访问实例上的属性:
from enum import Flag, auto
class RefreshFlags(Flag):
NONE = 0
EVENTS = auto()
RESOURCES = auto()
BUILDINGS = auto()
DEFENSES = auto()
@property
def ALL(self):
retval = self.NONE
# One needs to access .__class__ here!
for member in self.__class__.__members__.values():
retval |= member
return retval
>>> RefreshFlags.EVENTS.ALL
<RefreshFlags.DEFENSES|BUILDINGS|RESOURCES|EVENTS: 15>
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,您都可以稍后进行比较:
>>> RefreshFlags.EVENTS & RefreshFlags.EVENTS.ALL
<RefreshFlags.EVENTS: 1>
Run Code Online (Sandbox Code Playgroud)
您在评论中表示您希望该ALL成员表现得像其他人一样,在这种情况下,我建议使用类装饰器:
def with_ALL_member(enumeration):
retval = enumeration(0) # in case NONE is not defined
for name, member in enumeration.__members__.items():
retval |= member
enumeration.ALL = retval
return enumeration
@with_ALL_member
class RefreshFlags(Flag):
NONE = 0
EVENTS = auto()
RESOURCES = auto()
BUILDINGS = auto()
DEFENSES = auto()
>>> RefreshFlags.EVENTS & RefreshFlags.ALL
<RefreshFlags.EVENTS: 1>
>>> RefreshFlags.DEFENSES & RefreshFlags.ALL
<RefreshFlags.DEFENSES: 8>
Run Code Online (Sandbox Code Playgroud)
类装饰器也可以用于其他枚举:)
根据 MSeifert 的回答,可以编写一个@classproperty装饰器,它允许您直接作为属性进行访问RefreshFlags.ALL(而不是作为常规方法或实例上的属性):
from enum import Flag, auto
from operator import or_
from functools import reduce
class classproperty:
def __init__(self, func):
self._func = func
def __get__(self, obj, owner):
return self._func(owner)
class RefreshFlags(Flag):
NONE = 0
EVENTS = auto()
RESOURCES = auto()
BUILDINGS = auto()
DEFENSES = auto()
@classproperty
def ALL(cls):
return reduce(or_, cls)
Run Code Online (Sandbox Code Playgroud)
您当然可以像示例中那样ALL()使用显式循环编写;for以上仅作为替代方案提供。
>>> RefreshFlags.ALL
<RefreshFlags.DEFENSES|BUILDINGS|RESOURCES|EVENTS: 15>
>>> RefreshFlags.ALL & RefreshFlags.BUILDINGS
<RefreshFlags.BUILDINGS: 4>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1281 次 |
| 最近记录: |