사용자 정의 클래스를 작성할 때 ==
및 !=
연산자 를 사용하여 동등성을 허용하는 것이 종종 중요합니다 . 파이썬에서는 각각 __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 비교 방법을 사용하면 특별한 단점이 있습니까?
참고 : A는 설명의 비트 – 때 __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
에서 파이썬이 항상 우선 기억 __ne__
은 AS뿐만 아니라, 기능을 문서 상태 :
비교 연산자 사이에는 내재 된 관계가 없습니다. 진실은
x==y
그것이x!=y
거짓 임을 암시하지 않습니다 . 따라서을 정의 할 때 연산자가 예상대로 작동하도록__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
파이썬 3 에서는 문서가 다음 과 같이 더 이상 필요하지 않습니다 .
기본적으로 결과가 아닌 경우 결과를
__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
참고 : Python 2에는 두 가지 종류의 클래스가 있습니다.
-
고전적인 스타일 (또는 이전 스타일 않음) 클래스 하지 상속
object
과 같이 선언class A:
,class A():
또는class A(B):
어디에서B
고전적인 스타일의 클래스입니다; -
상속받은 새로운 스타일의 클래스,또는새로운 스타일의 클래스
object
로 선언 된곳. Python 3에는,또는로 선언 된 새로운 스타일의 클래스 만있습니다.class A(object)
class A(B):
B
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 클래식 스타일 클래스 에 대한 ==
and !=
연산자 의 비전 역 문제를 해결하려면 피연산자 유형이 지원되지 않을 때 __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 NotImplemented:
return NotImplemented
return not x
피연산자가 관련이없는 유형 (상속 없음) 인 경우 및 연산자의 정류 가 필요한 경우 새 스타일 클래스에 대해서도 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()))
답변
당신은 모두를 오버라이드 (override) 할 필요는 없습니다 __eq__
그리고 __ne__
당신은 단지 오버라이드 할 수 __cmp__
있지만, 이것은 ==,! ==, <,> 등의 결과에 시사점을 만들 것입니다.
is
객체 식별 테스트 이는 a와 b가 모두 동일한 객체에 대한 참조를 보유하는 경우 is
a가 b 임을 의미 True
합니다. 파이썬에서는 항상 실제 객체가 아닌 변수의 객체에 대한 참조를 유지하므로 본질적으로 a가 b가되는 경우 객체의 객체는 동일한 메모리 위치에 있어야합니다. 이 행동을 어떻게 무시할 것인가?
편집 : __cmp__
파이썬 3에서 제거되었다는 것을 몰랐 으므로 피하십시오.
답변
이 답변에서 : 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
답변
나는 당신이 찾고있는 두 용어가 평등 (==)과 정체성 (is) 이라고 생각합니다 . 예를 들면 다음과 같습니다.
>>> 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