優雅な方への支援等価性("平等")にPythonの授業
-
23-08-2019 - |
質問
書き込みカスタムクラスでしばしば重要な同等性により、 ==
や !=
います。Pythonでは、この実施により、 __eq__
や __ne__
特殊な方法です。最も簡単な方法がその場で発音を確認することがこのためには、以下の方法:
class Foo:
def __init__(self, item):
self.item = item
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
いの優雅なの?だけに、これらの点は上記の使用方法の比較 __dict__
s?
注意:ビットの解明が __eq__
や __ne__
定義されていません、この挙動:
>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
False
それは、 a == b
評価 False
だから本当に運行 a is b
, は、試験のアイデンティティ(すなわち、" a
同じオブジェクトとして b
?").
時 __eq__
や __ne__
定義され、この挙動は、今後):
>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
True
解決
このことを考え簡単な問題:
class Number:
def __init__(self, number):
self.number = number
n1 = Number(1)
n2 = Number(1)
n1 == n2 # False -- oops
なので、Pythonによるデフォルトのオブジェクト識別子の比較業
id(n1) # 140400634555856
id(n2) # 140400634555920
を上書きする __eq__
機能のように問題解決:
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return False
n1 == n2 # True
n1 != n2 # True in Python 2 -- oops, False in Python 3
に Python2, 常に忘れず、オーバーライドの __ne__
機能として、 文書 状態:
ありません包含関係の中での比較。の 真実の
x==y
を意味するものではありませんx!=y
はfalseです。そのため、この を定義する__eq__()
, のあるものの定義__ne__()
その 事業者の振る舞いをします。
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
return not self.__eq__(other)
n1 == n2 # True
n1 != n2 # False
に Python3, この必要が無くなって 文書 状態:
デフォルトでは、
__ne__()
代表者へ__eq__()
および反転結果 ない限り、NotImplemented
.が示唆 関係の比較演算子は、例えば、真実 の(x<y or x==y)
を意味するものではありませんx<=y
.
が解決しないすべての課題です。を追加してみましょうサブクラス:
class SubNumber(Number):
pass
n3 = SubNumber(1)
n1 == n3 # False for classic-style classes -- oops, True for new-style classes
n3 == n1 # True
n1 != n3 # True for classic-style classes -- oops, False for new-style classes
n3 != n1 # False
注意: Python2は、二種類のクラス
クラシック-スタイル ( 古いスタイル 授業では、い ない からの継承
object
ととして宣言されるclass A:
,class A():
またはclass A(B):
場所B
クラシック-スタイルクラス新スタイル 授業は、いからは継承し
object
ととして宣言されるclass A(object)
またはclass A(B):
場所B
新型クラスです。Python3には新形式クラスとして宣言されるclass A:
,class A(object):
またはclass A(B):
.
のためのクラシック-スタイルの授業では、比較動作いメソッドを呼び出すの最初のオペランドは、新しい形式のクラスでは、常にメソッドを呼び出すのは、サブクラスのオペランド にかかわらず、オペランド.
こちらでは、場合に Number
クラシック-スタイルのクラス:
n1 == n3
電話n1.__eq__
;n3 == n1
電話n3.__eq__
;n1 != n3
電話n1.__ne__
;n3 != n1
電話n3.__ne__
.
ば Number
新型クラス:
- 両
n1 == n3
やn3 == n1
電話n3.__eq__
; - 両
n1 != n3
やn3 != n1
電話n3.__ne__
.
の非可換性を備えた問題の ==
や !=
事業者のためのPythonの2クラシック-スタイルの授業を __eq__
や __ne__
メソッドを返します NotImplemented
値がオペランドタイプには対応しておりません。の 文書 を定義する NotImplemented
としての価値:
数値手法と豊かな比較方法がこの値が返される場合 な実施の運用のためのオペランドを提供する。( 通訳を試した動作、またはその他の スクフォースメンバーによっては、オペレーター.) 真値はtrueです。
この場合、オペレーターの代表者の比較動作の 反射方法 の その他 オペランド.の 文書 定義の反映方法として、
が入れ替わ引数のバージョンのこれらの方法を使用する 左引数のオペレーションをサポートしないもの権利 引数は);むしろ、
__lt__()
や__gt__()
それぞれその他の 反射__le__()
や__ge__()
互反射、__eq__()
や__ne__()
自身のものです。
結果は以下のようなものです:
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is not NotImplemented:
return not x
return NotImplemented
戻る NotImplemented
値の代わりに False
あとは自分がすべきことなのだといかにも新しい形式のクラスの場合 可換性を備え の ==
や !=
事業者が望の場合はオペランドの関係(相続).
いいですか。必ずしもそうではありません。どのように多くのユニークな番号を指定いますか?
len(set([n1, n2, n3])) # 3 -- oops
セット使用のハッシュオブジェクトによるデスクトップのイベントを返しますハッシュの識別子は、オブジェクトです。んでみましょう。参照してください。:
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
len(set([n1, n2, n3])) # 1
その結果、こんな感じになっている(見、主張の検証):
class Number:
def __init__(self, number):
self.number = number
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is not NotImplemented:
return not x
return NotImplemented
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
class SubNumber(Number):
pass
n1 = Number(1)
n2 = Number(1)
n3 = SubNumber(1)
n4 = SubNumber(4)
assert n1 == n2
assert n2 == n1
assert not n1 != n2
assert not n2 != n1
assert n1 == n3
assert n3 == n1
assert not n1 != n3
assert not n3 != n1
assert not n1 == n4
assert not n4 == n1
assert n1 != n4
assert n4 != n1
assert len(set([n1, n2, n3, ])) == 1
assert len(set([n1, n2, n3, n4])) == 2
他のヒント
あなたが継承して注意する必要があります:
>>> class Foo:
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
>>> class Bar(Foo):pass
>>> b = Bar()
>>> f = Foo()
>>> f == b
True
>>> b == f
False
このように、より厳密なタイプをチェックします:
def __eq__(self, other):
if type(other) is type(self):
return self.__dict__ == other.__dict__
return False
それに加えて、あなたのアプローチは、それが特別な方法がそこにあるものだ、正常に動作します。
あなたが記述の方法は、私はいつもそれをやった方法です。それは完全に汎用的なので、あなたは常にミックスインクラスにその機能を抜け出すと、あなたはその機能が必要なクラスで、それを継承することができます。
class CommonEqualityMixin(object):
def __eq__(self, other):
return (isinstance(other, self.__class__)
and self.__dict__ == other.__dict__)
def __ne__(self, other):
return not self.__eq__(other)
class Foo(CommonEqualityMixin):
def __init__(self, item):
self.item = item
未直接の答えが、それは機会に冗長退屈のビットを節約するようにタックするのに十分な関連見えました。ドキュメントからストレートカット...
<時間>functools.total_ordering(CLS)の
を一つ以上の豊富な比較の注文方法を定義するクラスを考えると、このクラスのデコレータは休息を提供するのこれが可能豊富な比較操作のすべてを指定するのに労力を簡素化:ます。
クラスは__lt__()
、__le__()
、__gt__()
、又は__ge__()
のいずれかを定義しなければなりません。また、クラスは__eq__()
方法を提供する必要があります。
バージョン2.7の新
@total_ordering
class Student:
def __eq__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
あなたは、両方の__eq__
をオーバーライドする必要があり、あなたが唯一の__ne__
をオーバーライドすることができますが、これは==、!==、<、>などの結果に意義を行います__cmp__
ます。
is
テスト。これは、aとbが同じオブジェクトへの参照を保持し、両方の場合is
bは場合True
されることを意味します。 Pythonでは、あなたは常にそれらのオブジェクトが同じメモリ位置に配置する必要が真であるために、Bであるので、本質的にするために、変数ではない実際のオブジェクト内のオブジェクトへの参照を保持します。どのようにして最も重要なのは、なぜあなたは、この動作をオーバーライドして行くのでしょうか?
編集:私はそれを避けるためのpython 3から削除された__cmp__
を知りませんでした。
は、この回答から: https://stackoverflow.com/a/30676267/541136 の私は、ことを証明しているがそれは用語の__ne__
に__eq__
を定義するために正しいです - 代わりに
def __ne__(self, other):
return not self.__eq__(other)
あなたが使用する必要があります:
def __ne__(self, other):
return not self == other
私はを平等の(==)とのアイデンティティーの(ある)あなたが探している二つの用語があると思います。たとえばます:
>>> a = [1,2,3]
>>> b = [1,2,3]
>>> a == b
True <-- a and b have values which are equal
>>> a is b
False <-- a and b are not the same list object
試験は、本質的に、オブジェクトのメモリアドレスを返し、従ってオーバーロードでない組み込みの「ID()関数を使用して識別するためにテストする「」である。
しかし、クラスの等価性をテストする場合には、おそらくあなたのテストについて、もう少し厳しくし、あなただけのクラスのデータ属性を比較したい:
import types
class ComparesNicely(object):
def __eq__(self, other):
for key, value in self.__dict__.iteritems():
if (isinstance(value, types.FunctionType) or
key.startswith("__")):
continue
if key not in other.__dict__:
return False
if other.__dict__[key] != value:
return False
return True
このコードは、あなたのクラスの非機能データメンバを比較するだけでなく、何をしたい一般的であるプライベート何かをスキップします。平野古いPythonオブジェクトの場合、私は私のPOPOオブジェクトはすべて、余分な(そしてほとんどの場合、同一)のロジックの負担を運ばないので、__init__、__str__、__repr__と__eq__を実装して、基本クラスを持っています。
の代わりにサブクラス化/ミックスインを使用するので、私は一般的なクラスのデコレータを使用したい。
def comparable(cls):
""" Class decorator providing generic comparison functionality """
def __eq__(self, other):
return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
def __ne__(self, other):
return not self.__eq__(other)
cls.__eq__ = __eq__
cls.__ne__ = __ne__
return cls
使用方法:
@comparable
class Number(object):
def __init__(self, x):
self.x = x
a = Number(1)
b = Number(1)
assert a == b