如果你正在处理一个或多个类,你不能从内部更改的,则有一些通用且简单的方法可以执行此操作,而这些方法也不依赖于diff特定的库:
最简单,非常不安全的复杂对象方法
pickle.dumps(a) == pickle.dumps(b)
pickle
是用于Python对象的非常常见的序列化库,因此实际上可以对几乎所有内容进行序列化。在上面的代码段中,我正在将str
from序列化为a
fromb
。与下一种方法不同,该方法还具有对自定义类进行类型检查的优点。
最大的麻烦:由于特定的排序和[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
预先使用Python的内置方法。
return NotImplemented
(而不是提高NotImplementedError
)的使用感到好奇。此处涵盖了该主题:stackoverflow.com/questions/878943/…–