在Python中比较某些对象的两个实例是否相同的最佳方法是什么?我希望能够做类似的事情
例:
class MyClass:
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
Run Code Online (Sandbox Code Playgroud)
编辑:
进一步澄清这个问题.我想通过属性值进行比较,并制作一个更通用的解决方案
x = MyClass('foo', 'bar')
y = MyClass('foo', 'bar')
Run Code Online (Sandbox Code Playgroud)
MyClass方法看起来应该是这样的吗?
>>> x == y
False
Run Code Online (Sandbox Code Playgroud)
e-s*_*tis 318
像往常一样使用Python,它是KISS:
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
Run Code Online (Sandbox Code Playgroud)
它输出:
>>> x == y
True
Run Code Online (Sandbox Code Playgroud)
注意:请注意,在Python 3.0之前,您更有可能以相同的方式使用__eq__而不是__eq__.
Chr*_*her 46
您可以覆盖对象中的丰富比较运算符.
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
Run Code Online (Sandbox Code Playgroud)
像这样:
def __eq__(self, other):
return self._id == other._id
Run Code Online (Sandbox Code Playgroud)
小智 14
对于Python 3.7(及更高版本)中的数据类,比较对象实例的相等性是一项内置功能。
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
Run Code Online (Sandbox Code Playgroud)
如果您正在处理一个或多个无法从内部更改的类,则有一些通用且简单的方法可以做到这一点,并且不依赖于特定于差异的库:
pickle.dumps(a) == pickle.dumps(b)
Run Code Online (Sandbox Code Playgroud)
pickle是一个非常常见的 Python 对象序列化库,因此几乎可以序列化任何东西,真的。在上面的代码片段中,我将strfrom 序列化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:])
Run Code Online (Sandbox Code Playgroud)
现在不管你的对象是什么,深度平等都可以保证工作
>>> 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
Run Code Online (Sandbox Code Playgroud)
可比对象的数量也无关紧要
>>> c = RandomForestClassifier(max_depth=2, random_state=1000)
>>> deep_equals(a, b, c)
False
Run Code Online (Sandbox Code Playgroud)
我的用例是在 BDD 测试中检查一组不同的已经训练好的机器学习模型之间的深度相等性。这些模型属于一组不同的第三方库。当然,__eq__像这里的其他答案一样实施建议对我来说不是一个选择。
您可能处于这样一种情况,其中一个或多个被比较的自定义类没有__dict__实现。这是不常见的以任何手段,但它是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__()
Run Code Online (Sandbox Code Playgroud)
编辑:为了组织起见,我用return dict_from(obj). 这dict_from是一个非常通用的反射,用于容纳更模糊的库(我在看着你,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):
# 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()}
Run Code Online (Sandbox Code Playgroud)
请注意,对于具有不同顺序的相同键值对的对象,上述方法都不会产生True,如
>>> a = {'foo':[], 'bar':{}}
>>> b = {'bar':{}, 'foo':[]}
>>> pickle.dumps(a) == pickle.dumps(b)
False
Run Code Online (Sandbox Code Playgroud)
但是,如果您愿意,无论如何都可以sorted事先使用 Python 的内置方法。
根据您的具体情况,您可以这样做:
>>> vars(x) == vars(y)
True
Run Code Online (Sandbox Code Playgroud)
从对象的字段查看Python 字典
__eq__在你的班级实施该方法; 这样的事情:
def __eq__(self, other):
return self.path == other.path and self.title == other.title
Run Code Online (Sandbox Code Playgroud)
编辑:如果您希望对象比较相等,当且仅当它们具有相同的实例字典时:
def __eq__(self, other):
return self.__dict__ == other.__dict__
Run Code Online (Sandbox Code Playgroud)
总结一下:
__eq__而不是__cmp__,除非您运行python <= 2.0(__eq__已在2.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)
Run Code Online (Sandbox Code Playgroud)