4

クラスをハッシュ可能にする標準的な方法がいくつかあります。たとえば ( SOから借用):

# assume X has 2 attributes: attr_a and attr_b
class X:
  def __key(self):
    return (self.attr_a, self.attr_b)

  def __eq__(x, y):
    return isinstance(y, x.__class__) and x.__key() == y.__key()

  def __hash__(self):
    return hash(self.__key())

ここで、ハッシュ可能にしたいクラスがたくさんあるとします。それらはすべて不変であり、不変の属性を持ち、これらすべての属性をまとめてハッシュすることは許容されます (属性が多すぎるクラスの場合、ほとんどの衝突を回避するのに十分ないくつかの属性のみをハッシュする必要があります)。__key()クラスごとにメソッドを手動で記述することを避けることはできますか?

__key()__eq__、を定義する基本クラスを作成するのは良い考え__hash__でしょうか? 特に、入力する必要があるすべてのインスタンス属性を見つけることができるかどうか__hash__はわかりません。これは一般的に不可能であることはわかっていますが、この場合、オブジェクトについてより多くのことを想定できます (たとえば、オブジェクトは不変__init__です - 終了後、その属性はすべてハッシュ可能など)。

(継承階層が機能しない場合は、おそらくデコレータで機能しますか?)

4

2 に答える 2

4

self.__dict__インスタンスは、属性を次の場所に保存します。

>>> class Foo(object):
...     def __init__(self, foo='bar', spam='eggs'):
...         self.foo = foo
...         self.spam = spam
... 
>>> f = Foo()
>>> f.__dict__
{'foo': 'bar', 'spam': 'eggs'}

インスタンスにメソッドを保存しない場合、デフォルト.__key()は次のようになります。

def __key(self):
    return tuple(v for k, v in sorted(self.__dict__.items()))

ここで、項目を属性名で並べ替えます。呼び出しは、呼び出しtuple()に適した不変のシーケンスを返すことを保証しhash()ます。

values()より複雑な設定の場合は、 (スキップ関数など)によって返される型をテストするか、特定の属性パターンを使用するか、使用__slots__できる適切な属性をリストするために再利用する必要があります。

__hash__メソッドとメソッドを組み合わせると、__eq__すべての不変クラスの継承元となる適切な基本クラスになります。

于 2012-09-20T12:30:27.317 に答える
1

属性の規則を想定すれば、それを行うことができます。あなたの例では、属性が「attr_」で始まるため、非常に簡単です-したがって、__keyメソッドを次のように記述できます。

def __key(self):
    return tuple (getattr(self, attr) for attr in self.__dict__ if attr.startswith("attr_") )

ご覧のとおり、ジェネレーター式のフィルター条件に適用するために見つけることができるすべてのテストが、ニーズに適合します。

私があなたに提案できるのは、クラスで Python の__slots__ 機能を使用することです。これにより、属性名が見つけやすくなるだけでなく、不変オブジェクトがより効率的に使用され、メモリ フットプリントが小さくなります。

class X:
    __slots__ = ("a", "b", "c")
    def __key(self):
        return tuple (getattr(self, attr) for attr in self.__class__.__slots__ )

編集 OPからの最初のコメントへの回答:

もちろん、これは継承で機能します。オブジェクトのすべての属性を常に使用する場合は、式の「if」部分は必要ありません。関数を_key(各クラスに内部的に一意の名前を付ける代わりに__key) 上のクラスに記述します。すべてのクラスで機能します。

于 2012-09-20T12:31:12.700 に答える