dictキーはハッシュ可能でなければなりません。これは、不変のハッシュ値を持つことを意味します。dict値は変更可能である場合とそうでない場合があります。ただし、変更可能な場合、これは 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
これは最初の2つの答えで説明できると思います。
この点について私が知っていることはありません。
いくつかの追加の考え:
キーの動作を理解するために知っておくべき主なことが2つobject.__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__
同様に、何らかの方法でオブジェクトのメソッドを「チェック」して、それが変更可能かどうかを判断できる場合があります。