내부에서 변경할 수없는 하나 이상의 클래스를 다루는 경우 diff 관련 라이브러리에 의존하지 않는 일반적이고 간단한 방법이 있습니다.
가장 쉽고 안전하지 않은 복잡한 객체 방법
pickle.dumps(a) == pickle.dumps(b)
pickle
파이썬 객체를위한 매우 일반적인 직렬화 라이브러리이므로, 거의 모든 것을 직렬화 할 수 있습니다. 위의 스 니펫 str
에서 from을 serialized a
와 from을 비교합니다 b
. 다음 방법과 달리이 방법은 사용자 정의 클래스를 유형 검사하는 이점도 있습니다.
가장 큰 번거 로움 : 특정 순서 지정 및 [de / en] 코딩 방법으로 pickle
인해 동일한 객체에 대해 동일한 결과를 얻지 못할 수 있습니다 . 특히 더 복잡한 객체 (예 : 중첩 된 사용자 정의 클래스 인스턴스 목록)를 다룰 때 특히 일부 타사 라이브러리에서. 그러한 경우 다른 접근법을 권장합니다.
철저하고 안전한 모든 물체 방법
직렬화 가능한 객체를 제공하는 재귀 반사를 작성한 다음 결과를 비교할 수 있습니다.
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__
다른 답변과 같이 하면 옵션이 아니 었습니다.
모든 기초를 덮고
비교중인 하나 이상의 사용자 정의 클래스에 구현 이없는__dict__
시나리오에있을 수 있습니다 . 그것은 흔하지는 않지만 sklearn의 Random Forest 분류 자 내의 하위 유형의 경우입니다 <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__()
편집 : 조직을 위해 마지막 두 줄을 base_typed
로 return dict_from(obj)
바꾸고 더 모호한 라이브러리를 수용하기 위해 실제로 일반적인 반영을 구현했습니다 (Doc2Vec)
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):
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
그러나 원한다면 sorted
어쨌든 파이썬의 내장 메소드를 사용할 수 있습니다 .
return NotImplemented
(모금 대신) 사용에 대해 궁금했다NotImplementedError
. 그 주제는 여기에 덮여 : stackoverflow.com/questions/878943/...을