9

私はPythonオブジェクトを「どの深さでも不変」であると定義しています

  1. それは(名目上)不変です。
  2. 「コンテナ」オブジェクトの場合、「どの深さでも不変」なオブジェクトのみが含まれます。

たとえば((1, 2), (3, 4))、任意の深さで不変ですが、((1, 2), [3, 4])そうではありません (後者は、タプルであるため、「名目上」不変ですが)。

Python オブジェクトが「どの深さでも不変」かどうかをテストする合理的な方法はありますか?

最初の条件 (例えば、collections.Hashableクラスを使用し、不適切に実装されたメソッドの可能性を無視する__hash__) をテストするのは比較的簡単ですが、2 番目の条件をテストするのはより困難です。 「コンテンツ」を繰り返し処理しています...

ありがとう!

4

4 に答える 4

5

不変性の一般的なテストはありません。オブジェクトは、そのメソッドのいずれも基になるデータを変更できない場合にのみ不変です。

多くの場合、通常は不変性に依存するハッシュ可能性に関心があります。ハッシュ可能なコンテナは、その内容(つまり、タプルとフリーズセット)を再帰的にハッシュします。したがって、テストは実行hash(obj)に相当し、成功した場合は、深くハッシュ可能でした。

IOW、あなたのコードはすでに利用可能な最高のテストを使用しています:

>>> a = ((1, 2), (3, 4))
>>> b = ((1, 2), [3, 4])
>>> hash(a)
5879964472677921951
>>> hash(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
于 2011-11-26T00:35:24.150 に答える
3

次のようなものを探していると思います。

def deeply_hashable(obj):
    try:
        hash(obj)
    except TypeError:
        return False
    try:
        iter(obj)
    except TypeError:
        return True
    return all(deeply_hashable(o) for o in obj)

ここでの明らかな問題の 1 つは、 a をdict反復処理すると、関心のある値ではなく、常に不変であるキーを反復処理することです。もちろん、特別なケースを除いて、これを回避する簡単な方法はありませんdict-これは、同様に動作する可能性があるが、から派生していない他のクラスには役立ちませんdict。最後に、私はdelnanに同意します。これを行うための単純でエレガントな一般的な方法はありません。

于 2011-11-26T00:24:13.957 に答える
2

あなたが正確に何を探しているのかわかりません。しかし、サンプルデータを使用すると:

>>> a = ((1, 2), (3, 4))
>>> b = ((1, 2), [3, 4])
>>> isinstance(a, collections.Hashable)
True
>>> isinstance(b, collections.Hashable)
True

したがって、実際に使用collections.Hashableする方法はありません。でも、

>>> hash(a)
5879964472677921951
>>> hash(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

したがって、少なくともサンプル データのhash場合、オブジェクトがハッシュ可能かどうかを確認するには、を使用するだけで十分です。もちろん、質問ですでに指摘したように__hash__、 のサブクラスに対して が正しく実装されていない場合list、このチェックは機能しません。

于 2011-11-26T00:14:50.557 に答える
1

このようなテストを行うことは絶対に理にかなっています。

オブジェクトを 'deepcopy()-ing' (または手動で clone()-ing) する時間と、単純な参照代入を検討してください!

2 つのエンティティが 1 つの同じオブジェクトを所有する必要があると想像してください。

次に、不変性が検証できる場合にのみ、参照割り当てを使用しても安全です。

次のようなものを再帰的にテストすることを検討します

def check(Candidate):
    if isinstance(Candidate, (str, int, long)):
        return True
    elif isinstance(Candidate, tuple):
        return not any(not check(x) for x in Candidate)
    else:
        return False
于 2013-11-11T07:18:30.700 に答える