在Python中是否有一种方法可以检查两个对象的每个变量中是否包含相同的值?

36

如何检查两个实例是否属于同一个类?

class FooBar(object):
    __init__(self, param):
        self.param = param
        self.param_2 = self.function_2(param)
        self.param_3 = self.function_3()

它们是相同的吗?我所说的相同是指它们在所有变量中具有相同的值。

a = FooBar(param)
b = FooBar(param)

我想到了

if a == b:
    print "a and b are identical"!

这样做会不会有副作用?

我提出这个问题的背景是单元测试。我想达到类似于:

self.failUnlessEqual(self.my_object.a_function(), another_object)
6个回答

58

如果你想让 == 起作用,那么你需要在你的类中实现 __eq__ 方法来执行丰富比较。

如果你只想比较所有属性的相等性,你可以通过比较每个对象的 __dict__ 来简洁地完成:

class MyClass:

    def __eq__(self, other) : 
        return self.__dict__ == other.__dict__

16
请注意,比较 __eq__ 中的 __dict__ 可能会返回 True,即使 other 是一个完全不同的类,只要它具有相同的属性。我知道这是人为的。如果您希望防范此情况,请首先插入 if self.__class__ != other.__class__: return False - Lauritz V. Thaulow
如果我在我的类中实现了__eq__,并且我有一个实例列表L_1 = [obj_1, obj_2, obj_3]和另一个列表L_2 = [obj_1, obj_2, obj_3],那么L_1 == L_2会按预期工作吗? - Aufwind
简而言之,是的。请注意,它们不必是相同的对象,就像你定义的两个示例列表一样。它们可以是两个包含不同对象的列表,并且只要列表中的对象在顺序上比较相等,它们仍然是相等的。请注意,顺序是重要的。 - AJ.
如果其中一个属性包含数组作为成员,这将无法正常工作。 - Code Pope

8

对于Python 3.7及以上版本,您还可以使用dataclass轻松检查您想要的内容。例如:

from dataclasses import dataclass

@dataclass
class FooBar:
    param: str
    param2: float
    param3: int

a = Foobar("test_text",2.0,3)
b = Foobar("test_text",2.0,3)

print(a==b)

会返回True


6

对于任意对象,== 运算符只有在两个对象是同一个对象(即它们引用内存中的相同地址)时才会返回true。

要获得更多"定制化"的行为,您需要覆盖比较运算符,特别是 __eq__。尝试将以下内容添加到您的类中:

def __eq__(self, other):
    if self.param == other.param \
    and self.param_2 == other.param_2 \
    and self.param_3 == other.param_3:
        return True
    else:
        return False

请注意,如果参数本身是您定义的对象,则这些对象必须以类似的方式定义__eq__才能正常工作。

另一个需要注意的问题是,如果您尝试以我上面所做的方式将FooBar对象与其他类型的对象进行比较,Python将尝试访问其他类型对象的param、param_2和param_3属性,这会引发AttributeError。您可能需要首先使用isinstance(other, FooBar)检查要比较的对象是否是FooBar的实例。默认情况下不执行此操作,因为可能存在希望在不同类型之间返回True进行比较的情况。

有关丰富比较的更多信息,请参见Python文档

(这里可以整理所有参数的比较方式,但出于明确起见,我将其保留。)

有关一种更简洁的方法来比较所有参数而且不会引发属性错误,请参见AJ的答案。


1

如果您想在测试中使用此功能,只需验证简单对象的字段是否相等,请查看来自testfixtures的compare

from testfixtures import compare

compare(a, b)

1
根据Lutz的《学习Python》,"=="运算符测试值等价性,递归比较所有嵌套对象。而"is"运算符测试两个对象是否是同一个对象,即在内存中具有相同地址(相同指针值)。 除了小整数和简单字符串的缓存/重用外,像x = [1,2]和y = [1,2]这样的两个对象在值上是相等的 "==",但y "is" x返回false。对于两个浮点数x = 3.567和y = 3.567也是如此。这意味着它们的地址不同,换句话说,hex(id(x)) != hex(id(y))。

对于类对象,我们必须覆盖方法__eq__(),使得两个类A对象像x = A(1,[2,3])和y = A(1,[2,3])在内容上是相等的"=="。默认情况下,class object "=="只比较id,因此在这种情况下id(x) != id(y),所以x != y。 总之,如果x "is" y,则x == y,反之则不成立。

谢谢,可能将 y is x 放在代码标记中会更好。 - precursor

0
为了避免在添加或删除模型属性时忘记对您的__eq__函数进行适当更改的可能性,您可以按以下方式定义它。
def __eq__(self, other):
    if self.__class__ == other.__class__:
        fields = [field.name for field in self._meta.fields]
        for field in fields:
            if not getattr(self, field) == getattr(other, field):
                return False
        return True
    else:
        raise TypeError('Comparing object is not of the same type.')

通过这种方式,比较了所有的对象属性。现在你可以通过object.__eq__(other)或者object == other来检查属性是否相等。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接