元の質問:
(私の質問は Python 3.2+ に適用されますが、これが Python 2.7 以降に変更されているとは思えません。)
通常、オブジェクトを作成すると予想される式を使用するとします。例: [1,2,3]
; 42
; 'abc'
; range(10)
; True
; open('readme.txt')
; MyClass()
; lambda x : 2 * x
; 等
このような 2 つの式が異なる時間に実行され、「同じ値に評価される」(つまり、同じ型を持ち、等しいと比較される) とします。Python はどのような条件下で、2 つの式が実際に 2 つの異なるオブジェクトを作成することを保証する、私が個別オブジェクトと呼ぶものを提供しますか (つまり、 2 つのオブジェクトが と にバインドされ、両方が同時にスコープ内にあると仮定すると、 として評価されます) x is y
?False
x
y
可変型のオブジェクトについては、「個別のオブジェクトの保証」が保持されることを理解しています。
x = [1,2]
y = [1,2]
assert x is not y # guaranteed to pass
str
また、特定の不変型 ( 、int
) については保証が成り立たないことも知っています。他の特定の不変型 ( bool
、NoneType
) については、逆の保証が適用されます。
x = True
y = not not x
assert x is not y # guaranteed to fail
x = 2
y = 3 - 1
assert x is not y # implementation-dependent; likely to fail in CPython
x = 1234567890
y = x + 1 - 1
assert x is not y # implementation-dependent; likely to pass in CPython
しかし、他のすべての不変型についてはどうでしょうか。
特に、異なる時期に作成された 2 つのタプルが同じ ID を持つことはありますか?
私がこれに興味を持っている理由は、グラフ内のノードを のタプルとして表しint
、ドメイン モデルでは、任意の 2 つのノードが (同じ値のタプルで表されている場合でも) 区別されるようになっているためです。ノードのセットを作成する必要があります。異なる時点で作成されたタプルが個別のオブジェクトであることを Python が保証する場合、単純にサブクラス化tuple
して、同一性を意味するように再定義することができます。
class DistinctTuple(tuple):
__hash__ = tuple.__hash__
def __eq__(self, other):
return self is other
x = (1,2)
y = (1,2)
s = set(x,y)
assert len(s) == 1 # pass; but not what I want
x = DistinctTuple(x)
y = DistinctTuple(y)
s = set(x,y)
assert len(s) == 2 # pass; as desired
しかし、別の時点で作成されたタプルが明確であることが保証されていない場合、上記の方法は恐ろしい手法であり、ランダムに出現する可能性があり、複製や発見が非常に困難な潜在的なバグを隠してしまいます。その場合、サブクラス化は役に立ちません。実際には、余分な要素として一意の ID を各タプルに追加する必要があります。または、タプルをリストに変換することもできます。いずれにせよ、私はより多くのメモリを使用します。明らかに、元のサブクラス化ソリューションが安全でない場合を除き、これらの代替手段を使用しないことをお勧めします。
私の推測では、Python は、組み込みまたはユーザー定義の不変型に対して「個別オブジェクト保証」を提供していません。しかし、ドキュメントでそれについて明確な声明を見つけられませんでした。
更新 1:
@LuperRouch @larsmansこれまでの議論と回答に感謝します。ここに私がまだはっきりしていない最後の問題があります:
ユーザー定義型のオブジェクトを作成すると、既存のオブジェクトが再利用される可能性はありますか?
これが可能であれば、私が使用しているクラスがそのような動作を示す可能性があるかどうかを確認する方法を知りたいです。
これが私の理解です。ユーザー定義クラスのオブジェクトが作成されるときはいつでも、クラスの__new__()
メソッドが最初に呼び出されます。このメソッドがオーバーライドされた場合、プログラマーが既存のオブジェクトへの参照を返すことを妨げる言語は何もないため、「個別のオブジェクトの保証」に違反します。明らかに、クラス定義を調べることでそれを観察できます。
__new__()
ユーザー定義のクラスがオーバーライドされない場合(または明示的__new__()
に基本クラスに依存している場合) はどうなるかわかりません。私が書いたら
class MyInt(int):
pass
オブジェクトの作成は によって処理されint.__new__()
ます。これは、次のアサーションが失敗する場合があることを意味すると思います。
x = MyInt(1)
y = MyInt(1)
assert x is not y # may fail, since int.__new__() might return the same object twice?
しかし、私の CPython での実験では、そのような動作を実現できませんでした。これは、言語が をオーバーライドしないユーザー定義クラスに「個別のオブジェクト保証」を提供することを意味するの__new__
でしょうか、それとも単に任意の実装動作なのでしょうか?
更新 2:
私DistinctTuple
の実装は完全に安全であることが判明しましたが、DistinctTuple
ノードをモデル化するために使用するという私の設計思想が非常に悪いことがわかりました。
ID 演算子は言語で既に使用可能です。==
と同じように振る舞うことは、論理的にis
不必要です。
さらに悪いことに、==
何か役に立つことができたとしても、それを利用できないようにしました。たとえば、プログラムのどこかで、2 つのノードが同じ整数のペアで表されているかどうかを確認したい場合がよくあります。==
それには完璧だったでしょう-そして実際、それはデフォルトでそれが行うことです...
さらに悪いことに、ほとんどの人は==
、ユーザー定義のクラスであっても、ID ではなく「値」を比較することを実際に期待しています。身元だけを見る私のオーバーライドでは、彼らは気づかないうちに捕まってしまうでしょう。
最後に...再定義しなければならなかった唯一の理由は==
、同じタプル表現を持つ複数のノードをセットの一部にできるようにすることでした。これは間違った方法です。==
変更する必要があるのは動作ではなく、コンテナーの種類です! セットの代わりにマルチセットを使用する必要がありました。
要するに、私の質問は他の状況ではある程度の価値があるかもしれませんが、作成は私のユースケースにとってひどい考えであると確信してclass DistinctTuple
います (そして、有効なユースケースがまったくないのではないかと強く思っています)。