Skip to content

Commit 6061f2d

Browse files
authored
Merge pull request #4843 from janezd/variable-fix-hash
[FIX] Variable: Fix cases when equal variables had different hashes
2 parents 2433cc2 + 3863320 commit 6061f2d

File tree

2 files changed

+38
-25
lines changed

2 files changed

+38
-25
lines changed

Orange/data/tests/test_variable.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ def test_eq_with_compute_value(self):
163163
a._compute_value = Identity(a1)
164164
self.assertEqual(a, a)
165165
self.assertEqual(a, b)
166+
self.assertEqual(hash(a), hash(b))
166167

167168
b._compute_value = a.compute_value
168169
self.assertEqual(a, b)
@@ -206,6 +207,27 @@ def test_hash(self):
206207
b._compute_value = Identity(a2)
207208
self.assertEqual(hash(a), hash(b))
208209

210+
at = TimeVariable("a")
211+
b = ContinuousVariable("b")
212+
self.assertEqual(hash(a1), hash(a2))
213+
self.assertNotEqual(hash(a1), hash(b))
214+
self.assertNotEqual(hash(a1), hash(at))
215+
216+
def test_hash_eq(self):
217+
a = ContinuousVariable("a")
218+
b1 = ContinuousVariable("b", compute_value=Identity(a))
219+
b2 = ContinuousVariable("b2", compute_value=Identity(b1))
220+
b3 = ContinuousVariable("b")
221+
self.assertEqual(a, b2)
222+
self.assertEqual(b1, b2)
223+
self.assertEqual(a, b1)
224+
self.assertNotEqual(b1, b3)
225+
226+
self.assertEqual(hash(a), hash(b2))
227+
self.assertEqual(hash(b1), hash(b2))
228+
self.assertEqual(hash(a), hash(b1))
229+
self.assertNotEqual(hash(b1), hash(b3))
230+
209231

210232
def variabletest(varcls):
211233
def decorate(cls):
@@ -252,7 +274,6 @@ def test_val_from_str_add(self):
252274
self.assertEqual(var.val_from_str_add("F"), 0)
253275
self.assertEqual(var.val_from_str_add("N"), 2)
254276

255-
256277
def test_repr(self):
257278
var = DiscreteVariable.make("a", values=("F", "M"))
258279
self.assertEqual(

Orange/data/variable.py

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -347,34 +347,26 @@ def make_proxy(self):
347347
return var
348348

349349
def __eq__(self, other):
350-
# pylint: disable=protected-access,import-outside-toplevel
351-
352-
def to_match(var):
353-
if var._compute_value is None:
354-
return var
355-
elif isinstance(var._compute_value, Identity):
356-
return var._compute_value.variable
357-
return None
350+
if type(self) is not type(other):
351+
return False
358352

359-
from Orange.preprocess.transformation import Identity
360-
return type(self) is type(other) and (
361-
self.name == other.name
362-
and self._compute_value == other._compute_value
363-
or
364-
(self.compute_value or other.compute_value)
365-
and to_match(self) == to_match(other) != None)
353+
var1 = self._get_identical_source(self)
354+
var2 = self._get_identical_source(other)
355+
# pylint: disable=protected-access
356+
return var1.name == var2.name \
357+
and var1._compute_value == var2._compute_value
366358

367359
def __hash__(self):
368-
# Two variables that are not equal can have the same hash.
369-
# This happens if one has compute_value == Identity and the other
370-
# doesn't have compute_value, or they have a different Identity.
371-
# Having the same hash while not being equal is of course allowed.
372-
# pylint: disable=import-outside-toplevel
360+
var = self._get_identical_source(self)
361+
return hash((var.name, type(self), var._compute_value))
362+
363+
@staticmethod
364+
def _get_identical_source(var):
365+
# pylint: disable=protected-access,import-outside-toplevel
373366
from Orange.preprocess.transformation import Identity
374-
compute_value = self._compute_value
375-
if isinstance(self._compute_value, Identity):
376-
compute_value = None
377-
return hash((self.name, type(self), compute_value))
367+
while isinstance(var._compute_value, Identity):
368+
var = var._compute_value.variable
369+
return var
378370

379371
@classmethod
380372
def make(cls, name, *args, **kwargs):

0 commit comments

Comments
 (0)