# HG changeset patch # User Paul Boddie # Date 1484151752 -3600 # Node ID d2e12678f77b16746ca126ddc53e76a3c588e1c4 # Parent 088b8ad14e33169087c728bad2e020022011d7a6 Extended hashing to tuples as well as strings. Fixed sequence comparisons by testing for TypeError and returning NotImplemented when sequences are compared to non-sequence objects. diff -r 088b8ad14e33 -r d2e12678f77b lib/__builtins__/sequence.py --- a/lib/__builtins__/sequence.py Wed Jan 11 12:26:04 2017 +0100 +++ b/lib/__builtins__/sequence.py Wed Jan 11 17:22:32 2017 +0100 @@ -19,6 +19,7 @@ this program. If not, see . """ +from __builtins__.int import maxint from native import isinstance as _isinstance class itemaccess: @@ -156,6 +157,30 @@ return 0 +class hashable(itemaccess): + + "An abstract class providing support for hashable sequences." + + _p = maxint / 32 + _a = 31 + + def _hashvalue(self, fn): + + """ + Return a value for hashing purposes for the sequence using the given + 'fn' on each item. + """ + + result = 0 + l = self.__len__() + i = 0 + + while i < l: + result = (result * self._a + fn(self.__get_single_item__(i))) % self._p + i += 1 + + return result + class sequence(itemaccess): "A common base class for sequence types." @@ -214,6 +239,18 @@ "Return whether this sequence is equal to 'other'." + try: + return self._eq(other) + except TypeError: + return NotImplemented + + def _eq(self, other): + + """ + Return whether this sequence is equal to 'other' sequence. Note that + this method will raise a TypeError if 'other' is not a sequence. + """ + # Sequences must have equal lengths to be equal. n = self.__len__() diff -r 088b8ad14e33 -r d2e12678f77b lib/__builtins__/str.py --- a/lib/__builtins__/str.py Wed Jan 11 12:26:04 2017 +0100 +++ b/lib/__builtins__/str.py Wed Jan 11 17:22:32 2017 +0100 @@ -19,20 +19,16 @@ this program. If not, see . """ -from __builtins__.int import maxint from __builtins__.operator import _negate -from __builtins__.sequence import itemaccess +from __builtins__.sequence import hashable, itemaccess from __builtins__.types import check_int from native import str_add, str_lt, str_gt, str_eq, str_len, str_nonempty, \ str_substr -class basestring(itemaccess): +class basestring(hashable): "The base class for all strings." - _p = maxint / 32 - _a = 31 - def __init__(self, other=None): "Initialise the string, perhaps from 'other'." @@ -62,15 +58,7 @@ "Return a value for hashing purposes." - result = 0 - l = self.__len__() - i = 0 - - while i < l: - result = (result * self._a + ord(self.__get_single_item__(i))) % self._p - i += 1 - - return result + return self._hashvalue(ord) def _binary_op(self, op, other): diff -r 088b8ad14e33 -r d2e12678f77b lib/__builtins__/tuple.py --- a/lib/__builtins__/tuple.py Wed Jan 11 12:26:04 2017 +0100 +++ b/lib/__builtins__/tuple.py Wed Jan 11 17:22:32 2017 +0100 @@ -20,11 +20,11 @@ """ from __builtins__.iterator import itemiterator -from __builtins__.sequence import sequence +from __builtins__.sequence import hashable, sequence from native import list_element, list_init, list_len, list_setsize, \ list_setelement -class tuple(sequence): +class tuple(sequence, hashable): "Implementation of tuple." @@ -47,6 +47,12 @@ list_setelement(self.__data__, i, arg) i += 1 + def __hash__(self): + + "Return a hashable value for the tuple." + + return self._hashvalue(hash) + def __getslice__(self, start, end=None): "Return a slice starting from 'start', with the optional 'end'." diff -r 088b8ad14e33 -r d2e12678f77b tests/dict.py --- a/tests/dict.py Wed Jan 11 12:26:04 2017 +0100 +++ b/tests/dict.py Wed Jan 11 17:22:32 2017 +0100 @@ -5,37 +5,56 @@ for key, value in d.items(): return value -d = {10 : "a", 20 : "b", "c" : 30} +d = {10 : "a", 20 : "b", "c" : 30, (1, 2) : "d"} +print "# d: ", print d print d[10] # a print d[20] # b print d["c"] # 30 +print d[(1, 2)] # d try: print d[30] # should fail with an exception except KeyError, exc: print "d[30]: key not found", exc.key l = f(d) +print "# l: ", print l print 10 in l # True print 20 in l # True print "c" in l # True +print "d" in l # False print 30 in l # False +print (1, 2) in l # True l = d.values() +print "# l: ", print l print "a" in l # True print "b" in l # True +print "c" in l # False +print "d" in l # True print 30 in l # True -print "c" in l # False +print (1, 2) in l # False -v = g(d) # either "a" or "b" or 30 +v = g(d) # either "a" or "b" or 30 or "d" +print "# v: ", print v -print v == "a" or v == "b" or v == 30 # True -print v == 10 or v == 20 or v == "c" # False +print v == "a" or v == "b" or v == 30 or v == "d" # True +print v == 10 or v == 20 or v == "c" or v == (1, 2) # False l = d.items() +print "# l: ", print l print (10, "a") in l # True print ("c", 30) in l # True +print ((1, 2), "d") in l # True print (10, "b") in l # False + +# Try to put a list key in a dictionary. + +try: + d[[1, 2]] = "e" + print d[[1, 2]] +except TypeError: + print "d[[1, 2]]: key not appropriate"