속성별로 동일한 개체 인스턴스 비교
이 있다MyClass
변수가 되어 있습니다.foo
★★★★★★★★★★★★★★★★★」bar
:
class MyClass:
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
, 각이 같습니다.foo
★★★★★★★★★★★★★★★★★」bar
:
x = MyClass('foo', 'bar')
y = MyClass('foo', 'bar')
은 Python을 한다.False
:
>>> x == y
False
어떻게 하면 비단뱀이 이 두 개체를 동등하다고 여길 수 있을까요?
다음 방법을 구현해야 합니다.
class MyClass:
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
def __eq__(self, other):
if not isinstance(other, MyClass):
# don't attempt to compare against unrelated types
return NotImplemented
return self.foo == other.foo and self.bar == other.bar
이제 출력:
>>> x == y
True
「 」를 실장하고 있는 것에 .__eq__
클래스 인스턴스는 자동으로 캐시 불능이 됩니다.즉, 세트와 딕트에 저장할 수 없습니다.타입을 「」의 경우).foo
★★★★★★★★★★★★★★★★★」bar
오브젝트의 라이프 타임 내에 값이 변경될 수 있습니다).인스턴스를 캐시 불능 상태로 두는 것이 좋습니다.
불변 유형을 모델링하는 경우 데이터 모델 후크도 구현해야 합니다.
class MyClass:
...
def __hash__(self):
# necessary for instances to behave sanely in dicts and sets.
return hash((self.foo, self.bar))
방법, ' 스루프 스루프 스루프 스루프 스루프 스루프 스루프 스루프 스루프 스루프 스루프 스루프 스루프 스루프 스루프'__dict__
하는 것은. - because the the the the the the the the the the the the the the the the the the the the the. - 결 and and and and and and and and and and and and and and and and and and and and and and and and and and and and and and and. 왜냐하면__dict__
에는 비교할 수 없거나 캐시할 수 없는 유형이 포함되어 있을 수 있습니다.
주의: Python 3 이전 버전에서는 다음과 같은 기능을 사용할 필요가 있습니다.__eq__
Python 2 사용자는 Python 2에서 부등식에 대한 합리적인 기본 동작(즉, 평등의 결과를 반전시키는 것)이 자동으로 생성되지 않기 때문에 Python 2 사용자도 구현하기를 원할 수 있습니다.
개체에서 리치 비교 연산자를 재정의합니다.
class MyClass:
def __lt__(self, other):
# return comparison
def __le__(self, other):
# return comparison
def __eq__(self, other):
# return comparison
def __ne__(self, other):
# return comparison
def __gt__(self, other):
# return comparison
def __ge__(self, other):
# return comparison
다음과 같이 합니다.
def __eq__(self, other):
return self._id == other._id
내부에서 변경할 수 없는 클래스를 하나 이상 취급하는 경우, 다른 특정 라이브러리에 의존하지 않는 일반적이고 간단한 방법이 있습니다.
매우 복잡한 오브젝트에는 가장 쉽고 안전하지 않은 방법
pickle.dumps(a) == pickle.dumps(b)
pickle
Python serialization lib는 serialization lib를 serialization으로 지정합니다.str
(시리얼라이즈)에서a
에서 온 것과 함께b
다음 방법과는 달리 이 방법은 사용자 정의 클래스를 유형으로 확인할 수 있는 장점이 있습니다.
가장 큰 문제: 특정 주문 및 [de/en] 코딩 방식 때문에, 특히 일부 서드파티 lib에서 자주 볼 수 있는 것처럼 더 복잡한 객체(예: 중첩된 사용자 지정 클래스 인스턴스 목록)를 처리할 때 동일한 결과를 얻을 수 없습니다.이러한 경우에는 다른 방법을 권장합니다.
철저하고 모든 오브젝트에 안전한 방법
연속 가능한 개체를 제공하는 재귀 반사를 작성하고 결과를 비교할 수 있습니다.
from collections.abc import Iterable
BASE_TYPES = [str, int, float, bool, type(None)]
def base_typed(obj):
"""Recursive reflection method to convert any object property into a comparable form.
"""
T = type(obj)
from_numpy = T.__module__ == 'numpy'
if T in BASE_TYPES or callable(obj) or (from_numpy and not isinstance(T, Iterable)):
return obj
if isinstance(obj, Iterable):
base_items = [base_typed(item) for item in obj]
return base_items if from_numpy else T(base_items)
d = obj if T is dict else obj.__dict__
return {k: base_typed(v) for k, v in d.items()}
def deep_equals(*args):
return all(base_typed(args[0]) == base_typed(other) for other in args[1:])
이제 목적이 무엇이든 간에 완전한 평등이 보장됩니다.
>>> from sklearn.ensemble import RandomForestClassifier
>>>
>>> a = RandomForestClassifier(max_depth=2, random_state=42)
>>> b = RandomForestClassifier(max_depth=2, random_state=42)
>>>
>>> deep_equals(a, b)
True
비교 가능한 제품의 수 또한 중요하지 않습니다.
>>> c = RandomForestClassifier(max_depth=2, random_state=1000)
>>> deep_equals(a, b, c)
False
이 사용 사례는 BDD 테스트 내에서 이미 교육받은 다양한 머신 러닝 모델 간의 완전한 평등을 확인하는 것이었습니다.이 모델들은 다양한 서드파티 립에 속했습니다.확실히 실장하고 있다__eq__
다른 답변처럼 제게는 선택사항이 아니었음을 시사하는 바입니다
모든 베이스를 커버하다
비교 대상 커스텀클래스가 1개 이상 실장되어 있지 않은 경우가 있습니다.이것은 일반적인 것은 아니지만 sklearn의 랜덤 포레스트 분류자 내의 서브타입의 경우입니다.<type 'sklearn.tree._tree.Tree'>
이러한 상황을 케이스 바이 케이스로 취급한다.구체적으로는, 피해 타입의 내용을, 인스턴스의 대표 정보를 얻을 수 있는 방법(이 경우는,__getstate__
이 , 의 두 에서 마지막 행까지.base_typed
d = obj if T is dict else obj.__dict__ if '__dict__' in dir(obj) else obj.__getstate__()
를 집집으로 했습니다.return dict_from(obj)
여기.dict_from
는, 한 립 에하기 위해서 , 정말로 입니다.
def isproperty(prop, obj):
return not callable(getattr(obj, prop)) and not prop.startswith('_')
def dict_from(obj):
"""Converts dict-like objects into dicts
"""
if isinstance(obj, dict):
# Dict and subtypes are directly converted
d = dict(obj)
elif '__dict__' in dir(obj):
# Use standard dict representation when available
d = obj.__dict__
elif str(type(obj)) == 'sklearn.tree._tree.Tree':
# Replaces sklearn trees with their state metadata
d = obj.__getstate__()
else:
# Extract non-callable, non-private attributes with reflection
kv = [(p, getattr(obj, p)) for p in dir(obj) if isproperty(p, obj)]
d = {k: v for k, v in kv}
return {k: base_typed(v) for k, v in d.items()}
위의 방법 중 어느 것도 양보하지 않는 것에 주의해 주십시오.True
: 키와 값의 페어가 다른 오브젝트)의 경우
>>> a = {'foo':[], 'bar':{}}
>>> b = {'bar':{}, 'foo':[]}
>>> pickle.dumps(a) == pickle.dumps(b)
False
Python의 Python을 할 수 .sorted
방법을 미리 알아둬야 합니다.
「 」의 실장__eq__
을 사용하다
def __eq__(self, other):
return self.path == other.path and self.title == other.title
Edit(편집): 동일한 인스턴스 사전이 있는 경우에만 동일한 개체를 비교하려면 다음과 같이 하십시오.
def __eq__(self, other):
return self.__dict__ == other.__dict__
Python 3.7 이상의 Dataclasses에서는 객체 인스턴스 비교가 내장된 기능입니다.
Python 3.6에서는 데이터 클래스용 백포트를 사용할 수 있습니다.
(Py37) nsc@nsc-vbox:~$ python
Python 3.7.5 (default, Nov 7 2019, 10:50:52)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from dataclasses import dataclass
>>> @dataclass
... class MyClass():
... foo: str
... bar: str
...
>>> x = MyClass(foo="foo", bar="bar")
>>> y = MyClass(foo="foo", bar="bar")
>>> x == y
True
요약:
- 하는 것이
__eq__
__cmp__
python <2하고 있는 예: python <= 2.0 (예: python <= 2.0).__eq__
는 2.12에 되었습니다.1 ★★★★★★★★★★★★★★★★★」 - 구현해
__ne__
(는)과 같은 이어야 합니다.return not self.__eq__(other)
★★★★★★★★★★★★★★★★★」return not self == other
우우 、 한우를외)))))) - 연산자는 비교하는 각 커스텀클래스에 실장되어 있어야 합니다(아래 예 참조).
None이 될 수 있는 개체와 비교하려면 개체를 구현해야 합니다.통역사는 그것을 추측할 수 없다...(아래 예 참조)
class B(object): def __init__(self): self.name = "toto" def __eq__(self, other): if other is None: return False return self.name == other.name class A(object): def __init__(self): self.toto = "titi" self.b_inst = B() def __eq__(self, other): if other is None: return False return (self.toto, self.b_inst) == (other.toto, other.b_inst)
특정 케이스에 따라 다음 작업을 수행할 수 있습니다.
>>> vars(x) == vars(y)
True
도 그 .__eq__
:
class MyClass:
def __init__(self, foo, bar, name):
self.foo = foo
self.bar = bar
self.name = name
def __eq__(self,other):
if not isinstance(other,MyClass):
return NotImplemented
else:
#string lists of all method names and properties of each of these objects
prop_names1 = list(self.__dict__)
prop_names2 = list(other.__dict__)
n = len(prop_names1) #number of properties
for i in range(n):
if getattr(self,prop_names1[i]) != getattr(other,prop_names2[i]):
return False
return True
객체의 인스턴스를 비교할 때 함수가 호출됩니다.
하지 않는 든지 ==를 할 수 .__cmp__
오브젝트의 함수입니다.
편집:
한 바와 같이, 적한 as as as as as 。__cmp__
기능은 3.0 이후 폐지되었습니다.대신 "풍부한 비교" 방법을 사용해야 합니다.
는 이것을 가가에 .test/utils
모듈화 됩니다.에는 ol.이것에 , 가 통과해, 「'dict」가 보증됩니다.
- 모든 속성은 그 상대와 같다
- 행잉 특성은 존재하지 않습니다(한 개체에만 존재하는 특성).
크네...섹시하지 않아...하지만 오보이는 성공했어!
def assertObjectsEqual(obj_a, obj_b):
def _assert(a, b):
if a == b:
return
raise AssertionError(f'{a} !== {b} inside assertObjectsEqual')
def _check(a, b):
if a is None or b is None:
_assert(a, b)
for k,v in a.items():
if isinstance(v, dict):
assertObjectsEqual(v, b[k])
else:
_assert(v, b[k])
# Asserting both directions is more work
# but it ensures no dangling values on
# on either object
_check(obj_a, obj_b)
_check(obj_b, obj_a)
돼요._assert
을 돼요.assert
하지만 실패했을 때 받는 메시지는 매우 도움이 되지 않습니다.
아래는 (제한된 테스트에서는) 2개의 오브젝트 계층을 상세하게 비교함으로써 동작합니다.에서 오브젝트 자체 또는 오브젝트 속성이 사전인 경우를 포함하여 다양한 사례를 처리합니다.
def deep_comp(o1:Any, o2:Any)->bool:
# NOTE: dict don't have __dict__
o1d = getattr(o1, '__dict__', None)
o2d = getattr(o2, '__dict__', None)
# if both are objects
if o1d is not None and o2d is not None:
# we will compare their dictionaries
o1, o2 = o1.__dict__, o2.__dict__
if o1 is not None and o2 is not None:
# if both are dictionaries, we will compare each key
if isinstance(o1, dict) and isinstance(o2, dict):
for k in set().union(o1.keys() ,o2.keys()):
if k in o1 and k in o2:
if not deep_comp(o1[k], o2[k]):
return False
else:
return False # some key missing
return True
# mismatched object types or both are scalers, or one or both None
return o1 == o2
이것은 매우 까다로운 코드이므로, 당신에게 도움이 되지 않을 수 있는 케이스를 코멘트에 추가해 주세요.
class Node:
def __init__(self, value):
self.value = value
self.next = None
def __repr__(self):
return str(self.value)
def __eq__(self,other):
return self.value == other.value
node1 = Node(1)
node2 = Node(1)
print(f'node1 id:{id(node1)}')
print(f'node2 id:{id(node2)}')
print(node1 == node2)
>>> node1 id:4396696848
>>> node2 id:4396698000
>>> True
하다를 사용하세요.setattr
를 들어 때 자체에 할 수 없는 .예를 들어 클래스를 가져올 때 클래스 자체에 무언가를 추가할 수 없는 경우 이 옵션을 사용할 수 있습니다.
setattr(MyClass, "__eq__", lambda x, y: x.foo == y.foo and x.bar == y.bar)
Atribute-by-Atribute의 비교를 취득해, 실패의 유무와 장소를 확인하는 경우는, 다음의 리스트의 이해를 사용할 수 있습니다.
[i for i,j in
zip([getattr(obj_1, attr) for attr in dir(obj_1)],
[getattr(obj_2, attr) for attr in dir(obj_2)])
if not i==j]
이 밖에도 PyCharm에서 디버깅할 때 한 줄씩 스퀴즈하여 "Evaluate Expression" 창에 입력할 수 있는 장점이 있습니다.
첫 번째 예(상기 7 참조)를 사용해 봤는데 ipython에서는 동작하지 않았습니다.cmp(obj1,obj2)는 2개의 동일한 오브젝트인스턴스를 사용하여 구현하면 "1"을 반환합니다.이상하게도 속성 값 중 하나를 수정하고 cmp(obj1,obj2)를 사용하여 다시 준비하면 오브젝트는 계속해서 "1"을 반환합니다. (슬쩍...)
좋습니다. 이제 두 개체를 반복하고 == 기호를 사용하여 각 특성을 비교해야 합니다.
==과(와) 비교할 때 클래스의 인스턴스가 non-instance가 됩니다.가장 좋은 방법은 cmp 함수를 당신의 클래스에 할당하는 것입니다.
콘텐츠로 비교하려면 cmp(obj1,obj2)를 사용하면 됩니다.
이 경우 cmp(doc1,doc2) 컨텐츠가 같으면 -1이 반환됩니다.
언급URL : https://stackoverflow.com/questions/1227121/compare-object-instances-for-equality-by-their-attributes
'sourcecode' 카테고리의 다른 글
서로 다른 두 데이터베이스의 테이블 간에 결합하시겠습니까? (0) | 2022.09.21 |
---|---|
SQLException이 잘못된 SQL 구문으로 인해 발생함 (0) | 2022.09.21 |
MySQL: 테이블이 여러 개입니까, 열이 많은 테이블이 한 개입니까? (0) | 2022.09.21 |
라라벨로 mysql에 접속하려면 어떻게 해야 하나요? (0) | 2022.09.21 |
PHP에서 블록 시도/캐치 중 예외가 검출되지 않음 (0) | 2022.09.21 |