我知道基类
Enum
和 IntEnum
。两者都非常有帮助,但我怀念标志操作的功能。我不希望这两个类实现我想要的功能。
让我们构建一个示例:
class NetlistKind(IntEnum):
Unknown = 0
LatticeNetlist = 1
QuartusNetlist = 2
XSTNetlist = 4
CoreGenNetlist = 8
All = 15
如您所见,我已经在使用
IntEnum
来获取此枚举的算术特征。最好有像 @unique
这样的东西来确保所有值都是 2 的幂。我可以通过分叉 enum.unique 来满足我的需要。 (我知道 All
是该规则的一个例外。)
这样的枚举是如何使用的?
filter = NetlistKind.LatticeNetlist | NetlistKind.QuartusNetlist
由于底层的 int 位操作是可能的,并且过滤器的内部值为 3。
如果有一个“在过滤器 Y 中设置标志 X”功能或者更好的运算符就更好了。我为
x in y
添加了一个神奇的功能:
@unique
class NetlistKind(IntEnum):
Unknown = 0
LatticeNetlist = 1
QuartusNetlist = 2
XSTNetlist = 4
CoreGenNetlist = 8
All = 15
def __contains__(self, item):
return (self.value & item.value) == item.value
使用示例:
....
def GetNetlists(self, filter=NetlistKind.All):
for entity in self._entities:
for nl in entity.GetNetlists():
if (nl.kind in filter):
yield nl
def GetXilinxNetlists(self):
return self.GetNetlists(NetlistKind.XSTNetlist | NetlistKind.CoreGenNetlist)
所以问题是:
开放功能:
__str__
我最近发布了一个开源包 py-flags 来解决这个问题。该库正是具有此功能,其设计深受 python3 枚举模块的影响。
关于它是否足够Pythonic来实现这样的标志类存在争议,因为它的功能与该语言提供的其他方法有很大的重叠(布尔变量、集合、具有布尔属性的对象或具有布尔项的字典的集合,...... )。出于这个原因,我觉得标志类的目的太狭窄和/或多余,无法进入标准库,但在某些情况下,它比前面列出的解决方案要好得多,因此可以使用“pip install”库方便。
使用 py-flags 模块,您的示例将如下所示:
from flags import Flags
class NetlistKind(Flags):
Unknown = 0
LatticeNetlist = 1
QuartusNetlist = 2
XSTNetlist = 4
CoreGenNetlist = 8
All = 15
上述内容可以进一步调整,因为使用库声明的标志类会自动提供两个“虚拟”标志:
NetlistKind.no_flags
和NetlistKind.all_flags
。这些使得已经声明的 NetlistKind.Unknown
和 NetlistKind.All
变得多余,因此我们可以将它们从声明中删除,但问题是 no_flags
和 all_flags
与您的命名约定不匹配。为了帮助实现这一点,我们在您的项目中声明一个标志基类而不是 flags.Flags
,您必须在项目的其余部分中使用它:
from flags import Flags
class BaseFlags(Flags):
__no_flags_name__ = 'Unknown'
__all_flags_name__ = 'All'
基于之前声明的基类(可以由项目中的任何标志进行子类化),我们可以将您的标志声明更改为:
class NetlistKind(BaseFlags):
LatticeNetlist = 1
QuartusNetlist = 2
XSTNetlist = 4
CoreGenNetlist = 8
这样
NetlistKind.Unknown
就会自动声明为零值。 NetlistKind.All
也在那里,它自动是您声明的所有标志的组合。可以使用/不使用这些虚拟标志来迭代枚举成员。您还可以声明别名(与先前声明的另一个标志具有相同值的标志)。
作为使用“函数调用样式”的替代声明(也由标准枚举模块提供):
NetlistKind = BaseFlags('NetlistKind', ['LatticeNetlist', 'QuartusNetlist',
'XSTNetlist', 'CoreGenNetlist'])
如果标志类声明了一些成员,那么它被认为是最终的。尝试子类化它会导致错误。从语义上来说,为了添加新成员或更改功能而允许对标志类进行子类化是不受欢迎的。
除此之外,flags 类以类型安全的方式提供您列出的运算符(布尔运算符、in、迭代等...)。我将在接下来的几天内完成 README.rst 以及对包接口的一些了解,但基本功能已经存在,并且经过了相当好的覆盖范围的测试。
我觉得这里有一个原生 Python 标志和按位运算的示例会很好。
from enum import Flag, auto
class FoodCategory(Flag):
FISH = auto()
SHELLFISH = auto()
TREE_NUTS = auto()
PEANUTS = auto()
WHEAT = auto()
RICE = auto()
MILK = auto()
BUTTER = auto()
ALLERGENS = TREE_NUTS | PEANUTS | WHEAT | MILK
if __name__ == "__main__":
DAIRY = FoodCategory.MILK | FoodCategory.BUTTER
print(f"{FoodCategory.MILK in DAIRY = }")
print(f"{FoodCategory.SHELLFISH in DAIRY = }")
print(f"{FoodCategory.WHEAT in FoodCategory.ALLERGENS = }")
print(f"{FoodCategory.SHELLFISH in FoodCategory.ALLERGENS = }")
输出:
FoodCategory.MILK in DAIRY = True
FoodCategory.SHELLFISH in DAIRY = False
FoodCategory.WHEAT in FoodCategory.ALLERGENS = True
FoodCategory.SHELLFISH in FoodCategory.ALLERGENS = False