flag/bit掩码操作是否有Pythonclass/enum?

Is there a Python class/enum for flag/bit mask operations?

我知道基数 类 EnumIntEnum。两者都非常有帮助,但我想念标志操作的功能。我不希望这两个 类 实现我想要的功能。

让我们构建一个例子:

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)

所以问题是:

开放功能:

我最近发布了一个针对这个问题的开源包 py-flags。该库正是具有此功能,其设计深受 python3 枚举模块的影响。

关于它是否足够 pythonic 来实现这样的标志存在争论 class 因为它的功能与语言提供的其他方法有很大的重叠(bool 变量的集合、集合、具有 bool 属性的对象或带有 bool 项的指令,...)。出于这个原因,我觉得标志 class 太狭窄了,目的 and/or 多余,无法进入标准库,但在某些情况下,它比之前列出的解决方案要好得多,所以有一个 "pip install"-able 库可以派上用场。

使用 py-flags 模块,您的示例如下所示:

from flags import Flags

class NetlistKind(Flags):
    Unknown = 0
    LatticeNetlist = 1
    QuartusNetlist = 2
    XSTNetlist = 4
    CoreGenNetlist = 8
    All = 15

上面的事情可以进一步调整,因为用库声明的标志 class 会自动提供两个 "virtual" 标志:NetlistKind.no_flagsNetlistKind.all_flags。这些使已声明的 NetlistKind.UnknownNetlistKind.All 变得多余,因此我们可以将它们从声明中删除,但问题是 no_flagsall_flags 与您的命名约定不匹配。为此,我们在您的项目中声明了一个标志基 class 而不是 flags.Flags,您将不得不在项目的其余部分中使用它:

from flags import Flags

class BaseFlags(Flags):
    __no_flags_name__ = 'Unknown'
    __all_flags_name__ = 'All'

基于先前声明的基础 class 可以被您项目中的任何标志子class,我们可以将您的标志声明更改为:

class NetlistKind(BaseFlags):
    LatticeNetlist = 1
    QuartusNetlist = 2
    XSTNetlist = 4
    CoreGenNetlist = 8

这样 NetlistKind.Unknown 自动声明为零值。 NetlistKind.All 也在那里,它自动是所有声明标志的组合。可以迭代枚举成员 with/without 这些虚拟标志。您还可以声明别名(与另一个先前声明的标志具有相同值的标志)。

作为使用 "function-call style" 的替代声明(也由标准枚举模块提供):

NetlistKind = BaseFlags('NetlistKind', ['LatticeNetlist', 'QuartusNetlist',
                                        'XSTNetlist', 'CoreGenNetlist'])

如果标志 class 声明了一些成员,那么它被认为是最终的。尝试 subclass 会导致错误。为了添加新成员或更改功能而允许子class 标记 class 在语义上是不受欢迎的。

除此之外,标志 class 以类型安全的方式提供您列出的运算符(布尔运算符、in、迭代等...)。我将在接下来的几天内完成 README.rst 以及包界面上的一些管道,但基本功能已经存在并且经过了很好的覆盖测试。

Python 3.6 添加了 Flag and IntFlag 支持通常的位操作。作为奖励,按位运算的结果值仍然是原始标志 class 的成员,并且是单例 [1].

aenum 库也有这个添加并且可以使用回到 Python 2.7。

[1] 3.6.0 中存在一个错误:如果伪标志成员是在线程中创建的,那么最终可能会重复;这在 3.6.1 中已修复(在 aenum 中从未存在)。