Skip to content

Instantly share code, notes, and snippets.

@sjkillen
Created November 17, 2019 23:21
Show Gist options
  • Save sjkillen/4a511bd41da3a2a7abfe37221beecf6d to your computer and use it in GitHub Desktop.
Save sjkillen/4a511bd41da3a2a7abfe37221beecf6d to your computer and use it in GitHub Desktop.
Lazy object hashing/equality checking for acyclic objects
def refineddir(obj):
return tuple(prop for prop in dir(obj) if not prop.startswith("_") and not hasattr(getattr(obj, prop), "__call__"))
class DeepComparator:
def __init__(self, wrap):
self.wrap = wrap
def __eq__(self, other):
if isinstance(self, DeepComparator):
self = self.wrap
if isinstance(other, DeepComparator):
other = other.wrap
props = refineddir(self)
if props != refineddir(other):
return False
for prop in props:
mine = getattr(self, prop)
if type(mine).__eq__ == object.__eq__:
mine = DeepComparator(mine)
theirs = getattr(other, prop)
if type(theirs).__eq__ == object.__eq__:
theirs = DeepComparator(theirs)
if mine != theirs:
return False
return True
def __hash__(self):
return hash(tuple(
getattr(self.wrap, prop) if type(getattr(self.wrap, prop)).__hash__ != object.__hash__ else DeepComparator(getattr(self.wrap, prop))
for prop in refineddir(self.wrap)
))
def value(self):
return self.wrap
class Bar:
def __init__(self, x):
self.x = x
def __repr__(self):
return f"Bar({self.x})"
class Foo:
def __init__(self, a, b):
self.a = a
self.b = Bar(b)
def __repr__(self):
return f"Foo({self.a}, {self.b})"
data = (Foo(11, 22), Bar(-1), Foo(11, 22), Foo(11, -1), Foo(0, 22), Foo(0, 0))
unique = tuple(map(DeepComparator.value, set(map(DeepComparator, data))))
print(unique)
assert DeepComparator(Foo(11, 11)) == DeepComparator(Foo(11, 11))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment