18

Is there any way to check if two objects have the same values, other than to iterate through their attributes and manually compare their values?

Mel
  • 5,837
  • 10
  • 37
  • 42
iman453
  • 9,105
  • 16
  • 54
  • 70
  • 1
    See [this similar question](http://stackoverflow.com/questions/390250/elegant-ways-to-support-equivalence-equality-in-python-classes) – GreenMatt Aug 23 '10 at 18:28

4 Answers4

11

@Joe Kington's solutions works if there is a __dict__ (some objects, including builtins, don't have one) and __eq__ works for all values of both dicts (a badly written __eq__ mayraise exceptions etc). But it is horribly unpythonic. It doesn't even handle nominal subtypes properly... much less structural subtypes (i.e. types that you can use in place/for duck-typing). Do not do this.

But usually you're better off with a hand-tailored __eq__ method that only compares some attributes that are significant. E.g. Rational should only compare numerator and denominator, nothing more.

  • 2
    "some meaningless comparisions may raise exceptions": nope, this applies only to comparisons involving ordering -- checking if `a` equals `b` (i.e., `a==b`, expressed or implied) **never** causes exceptions (unless you deliberately code a weird class overriding `__eq__` for the sole purpose of causing such exceptions;-). IOW, comparison for equality / inequality is **never** "meaningless". – Alex Martelli Aug 23 '10 at 18:29
  • @Alex Or unless somebody else codes such a class. Badly written classes occur pretty frequently. But, delnan seemed to imply that some builtin types behaved this way, which is indeed incorrect. – Devin Jeanpierre Aug 23 '10 at 18:37
  • objects with `__slots__` don't have `__dict__` either – John La Rooy Aug 23 '10 at 19:05
  • 2
    The problem is that testing equality can be completely senseless, so it'd be responsible to raise exceptions in that case! I more than once cursed at Python for not raising anything in `func==42` when I actually wanted to write `func()==42`. But we can never do this, even when the test is meaningless, because we want `something in (True, len, object)` to work at the same time. – Jochen Ritzel Aug 23 '10 at 20:12
6

To expound on delnan's answer:

_NOTFOUND = object()

class Rational(object):
    def __eq__(self, other):
        for attr in ['numerator', 'denominator']:
            v1, v2 = [getattr(obj, attr, _NOTFOUND) for obj in [self, other]]
            if v1 is _NOTFOUND or v2 is _NOTFOUND:
                return False
            elif v1 != v2:
                return False
        return True
habnabit
  • 9,906
  • 3
  • 32
  • 26
2

You can compare namedtuple directly.
Otherwise you have to define either your own rich comparisons __eq__ and possibly __ne__
or your own __cmp__

see the datamodel for more info

John La Rooy
  • 295,403
  • 53
  • 369
  • 502
-3

object1.__dict__ == object2.__dict__ Should be all you need, I think...

Edit: vars(object1) == vars(object2) is perhaps a bit more pythonic, though @delnan makes a valid point about objects (e.g. ints) that don't have a __dict__. I disagree that a custom __eq__ is a better approach for simple cases, though... Sometimes it's not worth the effort to go beyond quick and dirty, if quick and dirty perfectly does what you need, i.m.h.o.

Joe Kington
  • 275,208
  • 71
  • 604
  • 463
  • 3
    @Aaron, I'd agree that modifying `__dict__` directly is basically always wrong... I'd disagree that reading it directly is basically always wrong. Perhaps `vars` is a bit clearer, though... – Joe Kington Aug 23 '10 at 18:43
  • 3
    no, reading it is basically always wrong as well. It bypasses the descriptor protocol, not to mention it relies on an implementation detail. – habnabit Aug 23 '10 at 18:43
  • 1
    @Aaron - True, I guess I didn't think far enough into it. – Joe Kington Aug 23 '10 at 18:46
  • 1
    It also fails in the case of `__slots__`. `__slots__` shows that it's not just about implementation details of Python implementations, it's implementation details of objects within CPython. Accessing `__dict__` directly (or indirectly through `vars()`) is bad. – Devin Jeanpierre Aug 23 '10 at 18:46