web-dev-qa-db-ja.com

Python class / enum for flag / bit mask operations)はありますか?

基本クラスEnumIntEnumを知っています。どちらも非常に便利ですが、フラグ操作の機能がありません。これら2つのクラスが私の希望する機能を実装するとは思わない。

例を作ってみましょう:

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

ご覧のとおり、この列挙型の算術機能を取得するためにIntEnumを既に使用しています。すべての値が2のべき乗であることを確認するために@uniqueのようなものがあるといいでしょう。これを行うには、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)

だから質問は:

  • ビットフィールドを実装するより良い方法はありますか?
  • そのような1次元フィルターを実装するより良い方法はありますか?そのような単純なフィルター条件にラムダを使用したくないのですか?
  • そのようなソリューションはすでにPython標準ライブラリに含まれていますか?
  • この列挙型拡張を次のPythonバージョンに追加する方法は?:)

オープン機能:

  • すべてのアクティブなフラグのリストを__str__で返します
  • ...?
18
Paebbels

私は最近、この問題を目的としたオープンソースパッケージ py-flags を公開しました。そのライブラリーはまさにこの機能を備えており、その設計はpython3 enumモジュールに大きく影響されます。

その機能が言語によって提供される他のメソッド(bool変数、セット、bool属性を持つオブジェクト、またはbool項目を持つdictsなど)と非常に重複しているため、そのようなフラグクラスを実装するのに十分なPythonicかどうかについては議論があります。 。このため、フラグクラスは目的が狭すぎたり、冗長であったりして標準ライブラリに到達できないと感じていますが、場合によっては、上記のソリューションよりもはるかに優れているため、「pipインストール」が可能なライブラリを使用できます。便利で。

Py-flagsモジュールを使用すると、例は次のようになります。

from flags import Flags

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

ライブラリで宣言されたフラグクラスが2つの「仮想」フラグNetlistKind.no_flagsNetlistKind.all_flagsを自動的に提供するため、上記のことをさらに微調整することができます。これらは、宣言済みのNetlistKind.UnknownNetlistKind.Allを冗長にするため、宣言から除外できますが、no_flagsall_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もあり、自動的に宣言されたすべてのフラグの組み合わせになります。これらの仮想フラグの有無にかかわらず、列挙型メンバーを繰り返すことができます。エイリアス(以前に宣言された別のフラグと同じ値を持つフラグ)を宣言することもできます。

「関数呼び出しスタイル」を使用した代替宣言(標準のenumモジュールでも提供):

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

フラグクラスがいくつかのメンバーを宣言する場合、それは最終であると見なされます。サブクラス化しようとすると、エラーになります。新しいメンバーの追加や機能の変更を目的としてフラグクラスをサブクラス化できるようにすることは、意味的に望ましくありません。

これに加えて、フラグクラスはリストされた演算子(ブール演算子、入力、反復など)をタイプセーフな方法で提供します。今後数日でREADME.rstとパッケージインターフェイスの小さな配管を完了する予定ですが、基本的な機能はすでにそこにあり、十分にカバーされてテストされています。

14
pasztorpisti

Python 3.6には、通常のビット単位の演算をサポートする Flag および IntFlag が追加されました。おまけとして、ビット単位の演算から得られる値は、元のフラグクラスのメンバーであり、シングルトンです[1]。

aenum ライブラリにもこの追加があり、Python 2.7。

[1] 3.6.0にバグが存在します:疑似フラグメンバーがスレッドで作成されている場合、重複する可能性があります。これは3.6.1で修正されました(そしてaenumには存在しませんでした)。

31
Ethan Furman