35

このコードを検討してください:

a = {...} # a is an dict with arbitrary contents
b = a.copy()
  1. 変更可能性は辞書のキーと値でどのような役割を果たしますか?
  2. ある dict のキーまたは値への変更が他の dict に反映されないようにするにはどうすればよいですか?
  3. これは、辞書キーのハッシュ可能な制約とどのように関係していますか?
  4. Python 2.x と Python 3.x で動作に違いはありますか?

Python で型が変更可能かどうかを確認するにはどうすればよいですか?

4

7 に答える 7

21

1)ハッシュ可能であると同時に変更可能なユーザー定義クラスがない限り、キーは変更可能であってはなりません。あなたに課せられているのはそれだけです。ただし、ハッシュ可能で変更可能なオブジェクトを dict キーとして使用することは、悪い考えかもしれません。

2) 2 つの dict 間で値を共有しない。キーは不変でなければならないため、キーを共有しても問題ありません。モジュールの意味で、辞書をコピーするcopyことは間違いなく安全です。ここで dict コンストラクターを呼び出すことも機能します: b = dict(a). 不変の値を使用することもできます。

3) 組み込みの不変型はすべてハッシュ可能です。組み込みの可変型はすべてハッシュ可能ではありません。オブジェクトがハッシュ可能であるためには、それが変更されたとしても、その存続期間全体にわたって同じハッシュを持つ必要があります。

4)私が知っているわけではありません。2.xについて説明しています。

不変でなければ、型は可変です。組み込みの不変型である場合、型は不変です: strintlongboolfloattupleおよびおそらく私が忘れている他のいくつかの型。ユーザー定義型は常に変更可能です。

不変でなければ、オブジェクトは可変です。不変型のサブオブジェクトのみで再帰的に構成されている場合、そのオブジェクトは不変です。したがって、リストのタプルは可変です。タプルの要素を置き換えることはできませんが、リスト インターフェイスを介して要素を変更し、データ全体を変更することはできます。

于 2010-12-07T06:51:13.297 に答える
14

Python の言語レベルでは、可変性や不変性などは実際にはありません。一部のオブジェクト (文字列やタプルなど) はそれらを変更する方法を提供しないため、事実上不変ですが、それは純粋に概念的なものです。コードにも Python 自体にも、これを示す言語レベルのプロパティはありません。

不変性は、実際には辞書には関係ありません。変更可能な値をキーとして使用してもまったく問題ありません。重要なのは比較とハッシュです。オブジェクトは常にそれ自体と等しいままでなければなりません。例えば:

class example(object):
    def __init__(self, a):
        self.value = a
    def __eq__(self, rhs):
        return self.value == rhs.value
    def __hash__(self):
        return hash(self.value)

a = example(1)
d = {a: "first"}
a.data = 2
print d[example(1)]

ここでexampleは、不変ではありません。で変更していa.data = 2ます。それでも、問題なくハッシュのキーとして使用しています。なんで?変更しているプロパティは等価性には影響しません: ハッシュは変更されず、他のプロパティを無視してexample(1)常に と等しくなります。example(1)

これの最も一般的な用途はキャッシングとメモ化です。プロパティがキャッシュされているかどうかは論理的にオブジェクトを変更せず、通常は等価性に影響しません。

(ここでやめておきます。一度に 5 つの質問をしないでください。)

于 2010-12-07T09:11:34.440 に答える
6

モジュールコレクションには、MutableSequence、MutableSet、MutableMapping があります。これは、既製の型の可変性をチェックするために使用できます。

issubclass(TYPE, (MutableSequence, MutableSet, MutableMapping))

これをユーザー定義型で使用する場合は、型をそれらのいずれかから継承するか、仮想サブクラスとして登録する必要があります。

class x(MutableSequence):
    ...

また

class x:
    ...

abc.ABCMeta.register(MutableSequence,x)
于 2010-12-07T07:45:52.407 に答える
3

ハッシュ可能な型が不変であるという保証は実際にはありませんが、少なくとも、正しく実装__hash__するには、その型が自身のハッシュに関して、および等価性に関して不変である必要があります。これは、特定の方法で強制されるものではありません。

しかし、私たちはみな大人です。__hash__本当に意図しない限り、実装するのは賢明ではありません。大まかに言えば、型が実際に辞書のキーとして使用できる場合、それはそのように使用されることを意図しているということになります。

dict のようなものを探しているが、不変でもある場合namedtupleは、標準ライブラリにあるものからの最善の策かもしれません。確かに、これはあまり良い概算ではありませんが、出発点です。

于 2010-12-07T06:56:14.397 に答える
2
  1. dictキーはハッシュ可能でなければなりません。これは、不変のハッシュ値を持つことを意味します。dictは変更可能である場合とそうでない場合があります。ただし、変更可能な場合、これは 2 番目の質問に影響します。

  2. 「キーへの変更」は、2 つの辞書間で反映されません。文字列などの不変値への変更も反映されません。ユーザー定義クラスなどの変更可能なオブジェクトへの変更は反映されます。これは、オブジェクトが ID (参照) によって格納されるためです。

    class T(object):
      def __init__(self, v):
        self.v = v
    
    
    t1 = T(5)
    
    
    d1 = {'a': t1}
    d2 = d1.copy()
    
    
    d2['a'].v = 7
    d1['a'].v   # = 7
    
    
    d2['a'] = T(2)
    d2['a'].v   # = 2
    d1['a'].v   # = 7
    
    
    import copy
    d3 = copy.deepcopy(d2) # perform a "deep copy"
    d3['a'].v = 12
    d3['a'].v   # = 12
    d2['a'].v   # = 2
    
  3. これは最初の2つの答えで説明できると思います。

  4. この点について私が知っていることはありません。

いくつかの追加の考え:

キーの動作を理解するために知っておくべき主なことが2object.__hash__(self)ありますobject.__cmp__(self)。ドキュメントからの 1 つの重要なポイント: デフォルトでは、ユーザー定義オブジェクトのハッシュ関数は を返しid()ます。

次の例を検討してください。

class K(object):
  def __init__(self, x, y):
     self.x = x
     self.y = y
  def __hash__(self):
     return self.x + self.y

k1 = K(1, 2)
d1 = {k1: 3}
d1[k1] # outputs 3
k1.x = 5
d1[k1] # KeyError!  The key's hash has changed!
k2 = K(2, 1)
d1[k2] # KeyError!  The key's hash is right, but the keys aren't equal.
k1.x = 1
d1[k1] # outputs 3

class NewK(object):
  def __init__(self, x, y):
     self.x = x
     self.y = y
  def __hash__(self):
     return self.x + self.y
  def __cmp__(self, other):
     return self.x - other.x

nk1 = NewK(3, 4)
nd1 = {nk1: 5}
nd1[nk1] # outputs 5
nk2 = NewK(3, 7)
nk1 == nk2 # True!
nd1[nk2] # KeyError! The keys' hashes differ.
hash(nk1) == hash(nk2) # False
nk2.y = 4
nd1[nk2] # outputs 5

# Where this can cause issues:
nd1.keys()[0].x = 5
nd1[nk1] # KeyError! nk1 is no longer in the dict!
id(nd1.keys()[0]) == id(nk1)  # Yikes. True?!
nd1.keys()[0].x = 3
nd1[nk1]  # outputs 5
id(nd1.keys()[0]) == id(nk1)  # True!

ははるかに理解しやすく、dict はオブジェクトへの参照を格納します。ハッシュ可能のセクションを読んでください。文字列のようなものは不変です。それらを「変更」すると、変更した辞書が新しいオブジェクトを参照するようになります。変更可能なオブジェクトは「その場で変更」できるため、両方の辞書の値が変更されます。

d1 = {1: 'a'}
d2 = d1.copy()
id(d1[1]) == id(d2[1]) # True
d2[1] = 'z'
id(d1[1]) == id(d2[1]) # False

# the examples in section 2 above have more examples of this.

とにかく、ここにすべての主なポイントがあります:

  • keysの場合、気にするのはmutabilityではなく、hashability と comparabilityかもしれません。
  • 定義上、可変オブジェクトの値は参照を変更せずに変更できるため、値の可変性に注意してください。

これらの点のいずれかをテストする一般的な方法はないと思います。適合性のテストは、ユースケースによって異なります。たとえば、オブジェクトが__hash__比較 (__eq__または__cmp__) 関数を実装しているかどうかを確認するだけで十分な場合があります。__setattr__同様に、何らかの方法でオブジェクトのメソッドを「チェック」して、それが変更可能かどうかを判断できる場合があります。

于 2010-12-07T06:58:18.823 に答える
-2

辞書は、キーと値のペアの順序付けられていないセットです。キーは不変である必要があるため、ハッシュ可能です。オブジェクトがハッシュ可能かどうかを判断するには、次のhash()関数を使用できます。

>>> hash(1)
1
>>> hash('a')
12416037344
>>> hash([1,2,3])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> hash((1,2,3))
2528502973977326415
>>> hash({1: 1})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

一方、値は任意のオブジェクトにすることができます。オブジェクトが不変かどうかを確認する必要がある場合は、 を使用しますhash()

于 2010-12-07T06:57:04.427 に答える