DictWithLists.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. # The contents of this file are subject to the Python Software Foundation
  2. # License Version 2.3 (the License). You may not copy or use this file, in
  3. # either source code or executable form, except in compliance with the License.
  4. # You may obtain a copy of the License at http://www.python.org/license.
  5. #
  6. # Software distributed under the License is distributed on an AS IS basis,
  7. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  8. # for the specific language governing rights and limitations under the
  9. # License.
  10. #
  11. # These are some handy dict types:
  12. #
  13. # DictWithLists:
  14. # acts like a dict, but adding a key,value appends value to a list at that key
  15. # getting a value at a key returns the first value in the list
  16. # a key is only removed when the list is empty
  17. #
  18. # OrderedDict:
  19. # just like a dict, but d.keys() is in insertion order
  20. #
  21. # OrderedDictWithLists:
  22. # a combination of the two concepts that keeps lists at key locations in
  23. # insertion order
  24. #
  25. # by Greg Hazel
  26. # with code from David Benjamin and contributers
  27. from BTL.Lists import QList
  28. from BTL.obsoletepythonsupport import set
  29. class ReallyIterableDict(dict):
  30. # third level takes advantage of second level definitions
  31. def iteritems(self):
  32. for k in self:
  33. yield (k, self[k])
  34. def iterkeys(self):
  35. return self.__iter__()
  36. # fourth level uses definitions from lower levels
  37. def itervalues(self):
  38. for _, v in self.iteritems():
  39. yield v
  40. def values(self):
  41. return [v for _, v in self.iteritems()]
  42. def items(self):
  43. return list(self.iteritems())
  44. class DictWithLists(ReallyIterableDict):
  45. def __init__(self, d = None, parent = ReallyIterableDict):
  46. self.parent = parent
  47. # python dict() can't take None
  48. if d:
  49. self.parent.__init__(self, d)
  50. else:
  51. self.parent.__init__(self)
  52. def popitem(self):
  53. try:
  54. key = self.keys()[0]
  55. except IndexError:
  56. raise KeyError('popitem(): dictionary is empty')
  57. return (key, self.pop(key))
  58. def pop(self, key, *args):
  59. if key not in self and len(args) > 0:
  60. return args[0]
  61. l = self[key]
  62. data = l.popleft()
  63. # so we don't leak blank lists
  64. if len(l) == 0:
  65. self.parent.__delitem__(self, key)
  66. return data
  67. pop_from_row = pop
  68. def get_from_row(self, key):
  69. return self[key][0]
  70. def getrow(self, key):
  71. return self[key]
  72. def poprow(self, key):
  73. return self.parent.pop(self, key)
  74. def setrow(self, key, l):
  75. if len(l) == 0:
  76. return
  77. self.parent.__setitem__(self, key, l)
  78. def push(self, key, value):
  79. # a little footwork because the QList constructor is slow
  80. if key not in self:
  81. v = QList([value])
  82. self.parent.__setitem__(self, key, v)
  83. else:
  84. self[key].append(value)
  85. push_to_row = push
  86. def keys(self):
  87. return self.parent.keys(self)
  88. def total_length(self):
  89. t = 0
  90. for k in self.iterkeys():
  91. t += len(self.getrow(k))
  92. return t
  93. class DictWithInts(dict):
  94. def add(self, value):
  95. self.setdefault(value, 0)
  96. self[value] += 1
  97. def remove(self, value):
  98. if self[value] == 1:
  99. del self[value]
  100. else:
  101. self[value] -= 1
  102. class DictWithSets(DictWithLists):
  103. def pop(self, key, *args):
  104. if key not in self and len(args) > 0:
  105. return args[0]
  106. l = self[key]
  107. data = l.pop()
  108. # so we don't leak blank sets
  109. if len(l) == 0:
  110. self.parent.__delitem__(self, key)
  111. return data
  112. pop_from_row = pop
  113. def push(self, key, value):
  114. if key not in self:
  115. v = set([value])
  116. self.parent.__setitem__(self, key, v)
  117. else:
  118. self[key].add(value)
  119. push_to_row = push
  120. def remove_fom_row(self, key, value):
  121. l = self[key]
  122. l.remove(value)
  123. # so we don't leak blank sets
  124. if len(l) == 0:
  125. self.parent.__delitem__(self, key)
  126. # from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
  127. class OrderedDict(ReallyIterableDict):
  128. def __init__(self, d = None):
  129. self._keys = []
  130. # python dict() can't take None
  131. if d:
  132. ReallyIterableDict.__init__(self, dict)
  133. else:
  134. ReallyIterableDict.__init__(self)
  135. def __delitem__(self, key):
  136. ReallyIterableDict.__delitem__(self, key)
  137. self._keys.remove(key)
  138. def __setitem__(self, key, item):
  139. ReallyIterableDict.__setitem__(self, key, item)
  140. if key not in self._keys:
  141. self._keys.append(key)
  142. def clear(self):
  143. ReallyIterableDict.clear(self)
  144. self._keys = []
  145. def copy(self):
  146. newInstance = OrderedDict()
  147. newInstance.update(self)
  148. return newInstance
  149. def items(self):
  150. return zip(self._keys, self.values())
  151. def keys(self):
  152. return self._keys[:]
  153. def __iter__(self):
  154. return iter(self._keys)
  155. def pop(self, key):
  156. val = ReallyIterableDict.pop(self, key)
  157. self._keys.remove(key)
  158. return val
  159. def popitem(self):
  160. try:
  161. key = self._keys[0]
  162. except IndexError:
  163. raise KeyError('dictionary is empty')
  164. val = self.pop(key)
  165. return (key, val)
  166. def setdefault(self, key, failobj = None):
  167. if key not in self._keys:
  168. self._keys.append(key)
  169. return ReallyIterableDict.setdefault(self, key, failobj)
  170. def update(self, dict):
  171. for (key,val) in dict.items():
  172. self.__setitem__(key,val)
  173. def values(self):
  174. return map(self.get, self._keys)
  175. class OrderedDictWithLists(DictWithLists, OrderedDict):
  176. def __init__(self, dict = None, parent = OrderedDict):
  177. DictWithLists.__init__(self, dict, parent = parent)
  178. def __iter__(self):
  179. return iter(self._keys)
  180. if __name__=='__main__':
  181. d = DictWithLists()
  182. for i in xrange(50):
  183. for j in xrange(50):
  184. d.push(i, j)
  185. for i in xrange(50):
  186. for j in xrange(50):
  187. assert d.pop(i) == j
  188. od = OrderedDict()
  189. def make_str(i):
  190. return str(i) + "extra"
  191. for i in xrange(50):
  192. od[make_str(i)] = 1
  193. for i,j in zip(xrange(50), od.keys()):
  194. assert make_str(i) == j
  195. odl = OrderedDictWithLists()
  196. for i in xrange(50):
  197. for j in xrange(50):
  198. odl.push(make_str(i), j)
  199. for i in xrange(50):
  200. for j in xrange(50):
  201. assert odl.pop(make_str(i)) == j
  202. od = OrderedDict()
  203. od['2'] = [1,1,1,1,1]
  204. od['1'] = [2,2,2,2,2]
  205. od['3'] = [3,3,3,3,3]
  206. k = od.keys()[0]
  207. assert k == '2'
  208. odl = OrderedDictWithLists()
  209. odl.setrow('2', [1,1,1,1,1])
  210. odl.setrow('1', [2,2,2,2,2])
  211. odl.setrow('3', [3,3,3,3,3])
  212. k = odl.keys()[0]
  213. assert k == '2'
  214. od = OrderedDict()
  215. od['2'] = [1,1,1,1,1]
  216. od['1'] = [2,2,2,2,2]
  217. od['3'] = [3,3,3,3,3]
  218. r = []
  219. for k in od.iterkeys():
  220. r.append(k)
  221. assert r == ['2', '1', '3']
  222. odl = OrderedDictWithLists()
  223. odl.setrow('2', [1,1,1,1,1])
  224. odl.setrow('1', [2,2,2,2,2])
  225. odl.setrow('3', [3,3,3,3,3])
  226. r = []
  227. for k in odl.iterkeys():
  228. r.append(k)
  229. assert r == ['2', '1', '3']
  230. d = DictWithLists()
  231. d.push(4, 3)
  232. d.push(4, 4)
  233. d.push(4, 2)
  234. d.push(4, 1)
  235. assert d.poprow(4) == QList([3,4,2,1])