1 2import cython 3 4class Unhashable(object): 5 def __hash__(self): 6 raise TypeError('I am not hashable') 7 8class Hashable(object): 9 def __hash__(self): 10 return 1 11 def __eq__(self, other): 12 return isinstance(other, Hashable) 13 14class CountedHashable(object): 15 def __init__(self): 16 self.hash_count = 0 17 self.eq_count = 0 18 def __hash__(self): 19 self.hash_count += 1 20 return 42 21 def __eq__(self, other): 22 self.eq_count += 1 23 return id(self) == id(other) 24 25@cython.test_fail_if_path_exists('//AttributeNode') 26@cython.test_assert_path_exists('//PythonCapiCallNode') 27@cython.locals(d=dict) 28def setdefault1(d, key): 29 """ 30 >>> d = {} 31 >>> setdefault1(d, 1) 32 >>> len(d) 33 1 34 >>> setdefault1(d, 1) 35 >>> len(d) 36 1 37 >>> d[1] 38 >>> setdefault1(d, Unhashable()) 39 Traceback (most recent call last): 40 TypeError: I am not hashable 41 >>> len(d) 42 1 43 >>> h1 = setdefault1(d, Hashable()) 44 >>> len(d) 45 2 46 >>> h2 = setdefault1(d, Hashable()) 47 >>> len(d) 48 2 49 >>> d[Hashable()] 50 51 # CPython's behaviour depends on version and py_debug setting, so just compare to it 52 >>> py_hashed1 = CountedHashable() 53 >>> y = {py_hashed1: 5} 54 >>> py_hashed2 = CountedHashable() 55 >>> y.setdefault(py_hashed2) 56 57 >>> cy_hashed1 = CountedHashable() 58 >>> y = {cy_hashed1: 5} 59 >>> cy_hashed2 = CountedHashable() 60 >>> setdefault1(y, cy_hashed2) 61 >>> py_hashed1.hash_count - cy_hashed1.hash_count 62 0 63 >>> py_hashed2.hash_count - cy_hashed2.hash_count 64 0 65 >>> (py_hashed1.eq_count + py_hashed2.eq_count) - (cy_hashed1.eq_count + cy_hashed2.eq_count) 66 0 67 """ 68 return d.setdefault(key) 69 70@cython.test_fail_if_path_exists('//AttributeNode') 71@cython.test_assert_path_exists('//PythonCapiCallNode') 72@cython.locals(d=dict) 73def setdefault2(d, key, value): 74 """ 75 >>> d = {} 76 >>> setdefault2(d, 1, 2) 77 2 78 >>> len(d) 79 1 80 >>> setdefault2(d, 1, 2) 81 2 82 >>> len(d) 83 1 84 >>> l = setdefault2(d, 2, []) 85 >>> len(d) 86 2 87 >>> l.append(1) 88 >>> setdefault2(d, 2, []) 89 [1] 90 >>> len(d) 91 2 92 >>> setdefault2(d, Unhashable(), 1) 93 Traceback (most recent call last): 94 TypeError: I am not hashable 95 >>> h1 = setdefault2(d, Hashable(), 55) 96 >>> len(d) 97 3 98 >>> h2 = setdefault2(d, Hashable(), 66) 99 >>> len(d) 100 3 101 >>> d[Hashable()] 102 55 103 104 # CPython's behaviour depends on version and py_debug setting, so just compare to it 105 >>> py_hashed1 = CountedHashable() 106 >>> y = {py_hashed1: 5} 107 >>> py_hashed2 = CountedHashable() 108 >>> y.setdefault(py_hashed2, []) 109 [] 110 111 >>> cy_hashed1 = CountedHashable() 112 >>> y = {cy_hashed1: 5} 113 >>> cy_hashed2 = CountedHashable() 114 >>> setdefault2(y, cy_hashed2, []) 115 [] 116 >>> py_hashed1.hash_count - cy_hashed1.hash_count 117 0 118 >>> py_hashed2.hash_count - cy_hashed2.hash_count 119 0 120 >>> (py_hashed1.eq_count + py_hashed2.eq_count) - (cy_hashed1.eq_count + cy_hashed2.eq_count) 121 0 122 """ 123 return d.setdefault(key, value) 124