Mercurial > hg > martINI
comparison martini/odict.py @ 8:81aed4352851
make martini work with an ordered dictionary
| author | Jeff Hammel <jhammel@mozilla.com> |
|---|---|
| date | Wed, 24 Nov 2010 11:05:40 -0800 |
| parents | |
| children | 4faed08eb8d8 |
comparison
equal
deleted
inserted
replaced
| 7:7c8f23eae311 | 8:81aed4352851 |
|---|---|
| 1 # odict.py | |
| 2 # An Ordered Dictionary object | |
| 3 # Copyright (C) 2005 Nicola Larosa, Michael Foord | |
| 4 # E-mail: nico AT tekNico DOT net, fuzzyman AT voidspace DOT org DOT uk | |
| 5 | |
| 6 # This software is licensed under the terms of the BSD license. | |
| 7 # http://www.voidspace.org.uk/python/license.shtml | |
| 8 # Basically you're free to copy, modify, distribute and relicense it, | |
| 9 # So long as you keep a copy of the license with it. | |
| 10 | |
| 11 # Documentation at http://www.voidspace.org.uk/python/odict.html | |
| 12 # For information about bugfixes, updates and support, please join the | |
| 13 # Pythonutils mailing list: | |
| 14 # http://groups.google.com/group/pythonutils/ | |
| 15 # Comments, suggestions and bug reports welcome. | |
| 16 | |
| 17 """A dict that keeps keys in insertion order""" | |
| 18 from __future__ import generators | |
| 19 | |
| 20 __author__ = ('Nicola Larosa <nico-NoSp@m-tekNico.net>,' | |
| 21 'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>') | |
| 22 | |
| 23 __docformat__ = "restructuredtext en" | |
| 24 | |
| 25 __revision__ = '$Id: odict.py 129 2005-09-12 18:15:28Z teknico $' | |
| 26 | |
| 27 __version__ = '0.2.2' | |
| 28 | |
| 29 __all__ = ['OrderedDict', 'SequenceOrderedDict'] | |
| 30 | |
| 31 import sys | |
| 32 INTP_VER = sys.version_info[:2] | |
| 33 if INTP_VER < (2, 2): | |
| 34 raise RuntimeError("Python v.2.2 or later required") | |
| 35 | |
| 36 import types, warnings | |
| 37 | |
| 38 class OrderedDict(dict): | |
| 39 """ | |
| 40 A class of dictionary that keeps the insertion order of keys. | |
| 41 | |
| 42 All appropriate methods return keys, items, or values in an ordered way. | |
| 43 | |
| 44 All normal dictionary methods are available. Update and comparison is | |
| 45 restricted to other OrderedDict objects. | |
| 46 | |
| 47 Various sequence methods are available, including the ability to explicitly | |
| 48 mutate the key ordering. | |
| 49 | |
| 50 __contains__ tests: | |
| 51 | |
| 52 >>> d = OrderedDict(((1, 3),)) | |
| 53 >>> 1 in d | |
| 54 1 | |
| 55 >>> 4 in d | |
| 56 0 | |
| 57 | |
| 58 __getitem__ tests: | |
| 59 | |
| 60 >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2] | |
| 61 1 | |
| 62 >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4] | |
| 63 Traceback (most recent call last): | |
| 64 KeyError: 4 | |
| 65 | |
| 66 __len__ tests: | |
| 67 | |
| 68 >>> len(OrderedDict()) | |
| 69 0 | |
| 70 >>> len(OrderedDict(((1, 3), (3, 2), (2, 1)))) | |
| 71 3 | |
| 72 | |
| 73 get tests: | |
| 74 | |
| 75 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 76 >>> d.get(1) | |
| 77 3 | |
| 78 >>> d.get(4) is None | |
| 79 1 | |
| 80 >>> d.get(4, 5) | |
| 81 5 | |
| 82 >>> d | |
| 83 OrderedDict([(1, 3), (3, 2), (2, 1)]) | |
| 84 | |
| 85 has_key tests: | |
| 86 | |
| 87 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 88 >>> d.has_key(1) | |
| 89 1 | |
| 90 >>> d.has_key(4) | |
| 91 0 | |
| 92 """ | |
| 93 | |
| 94 def __init__(self, init_val=(), strict=False): | |
| 95 """ | |
| 96 Create a new ordered dictionary. Cannot init from a normal dict, | |
| 97 nor from kwargs, since items order is undefined in those cases. | |
| 98 | |
| 99 If the ``strict`` keyword argument is ``True`` (``False`` is the | |
| 100 default) then when doing slice assignment - the ``OrderedDict`` you are | |
| 101 assigning from *must not* contain any keys in the remaining dict. | |
| 102 | |
| 103 >>> OrderedDict() | |
| 104 OrderedDict([]) | |
| 105 >>> OrderedDict({1: 1}) | |
| 106 Traceback (most recent call last): | |
| 107 TypeError: undefined order, cannot get items from dict | |
| 108 >>> OrderedDict({1: 1}.items()) | |
| 109 OrderedDict([(1, 1)]) | |
| 110 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 111 >>> d | |
| 112 OrderedDict([(1, 3), (3, 2), (2, 1)]) | |
| 113 >>> OrderedDict(d) | |
| 114 OrderedDict([(1, 3), (3, 2), (2, 1)]) | |
| 115 """ | |
| 116 self.strict = strict | |
| 117 dict.__init__(self) | |
| 118 if isinstance(init_val, OrderedDict): | |
| 119 self._sequence = init_val.keys() | |
| 120 dict.update(self, init_val) | |
| 121 elif isinstance(init_val, dict): | |
| 122 # we lose compatibility with other ordered dict types this way | |
| 123 raise TypeError('undefined order, cannot get items from dict') | |
| 124 else: | |
| 125 self._sequence = [] | |
| 126 self.update(init_val) | |
| 127 | |
| 128 ### Special methods ### | |
| 129 | |
| 130 def __delitem__(self, key): | |
| 131 """ | |
| 132 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 133 >>> del d[3] | |
| 134 >>> d | |
| 135 OrderedDict([(1, 3), (2, 1)]) | |
| 136 >>> del d[3] | |
| 137 Traceback (most recent call last): | |
| 138 KeyError: 3 | |
| 139 >>> d[3] = 2 | |
| 140 >>> d | |
| 141 OrderedDict([(1, 3), (2, 1), (3, 2)]) | |
| 142 >>> del d[0:1] | |
| 143 >>> d | |
| 144 OrderedDict([(2, 1), (3, 2)]) | |
| 145 """ | |
| 146 if isinstance(key, types.SliceType): | |
| 147 # FIXME: efficiency? | |
| 148 keys = self._sequence[key] | |
| 149 for entry in keys: | |
| 150 dict.__delitem__(self, entry) | |
| 151 del self._sequence[key] | |
| 152 else: | |
| 153 # do the dict.__delitem__ *first* as it raises | |
| 154 # the more appropriate error | |
| 155 dict.__delitem__(self, key) | |
| 156 self._sequence.remove(key) | |
| 157 | |
| 158 def __eq__(self, other): | |
| 159 """ | |
| 160 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 161 >>> d == OrderedDict(d) | |
| 162 True | |
| 163 >>> d == OrderedDict(((1, 3), (2, 1), (3, 2))) | |
| 164 False | |
| 165 >>> d == OrderedDict(((1, 0), (3, 2), (2, 1))) | |
| 166 False | |
| 167 >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) | |
| 168 False | |
| 169 >>> d == dict(d) | |
| 170 False | |
| 171 >>> d == False | |
| 172 False | |
| 173 """ | |
| 174 if isinstance(other, OrderedDict): | |
| 175 # FIXME: efficiency? | |
| 176 # Generate both item lists for each compare | |
| 177 return (self.items() == other.items()) | |
| 178 else: | |
| 179 return False | |
| 180 | |
| 181 def __lt__(self, other): | |
| 182 """ | |
| 183 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 184 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) | |
| 185 >>> c < d | |
| 186 True | |
| 187 >>> d < c | |
| 188 False | |
| 189 >>> d < dict(c) | |
| 190 Traceback (most recent call last): | |
| 191 TypeError: Can only compare with other OrderedDicts | |
| 192 """ | |
| 193 if not isinstance(other, OrderedDict): | |
| 194 raise TypeError('Can only compare with other OrderedDicts') | |
| 195 # FIXME: efficiency? | |
| 196 # Generate both item lists for each compare | |
| 197 return (self.items() < other.items()) | |
| 198 | |
| 199 def __le__(self, other): | |
| 200 """ | |
| 201 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 202 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) | |
| 203 >>> e = OrderedDict(d) | |
| 204 >>> c <= d | |
| 205 True | |
| 206 >>> d <= c | |
| 207 False | |
| 208 >>> d <= dict(c) | |
| 209 Traceback (most recent call last): | |
| 210 TypeError: Can only compare with other OrderedDicts | |
| 211 >>> d <= e | |
| 212 True | |
| 213 """ | |
| 214 if not isinstance(other, OrderedDict): | |
| 215 raise TypeError('Can only compare with other OrderedDicts') | |
| 216 # FIXME: efficiency? | |
| 217 # Generate both item lists for each compare | |
| 218 return (self.items() <= other.items()) | |
| 219 | |
| 220 def __ne__(self, other): | |
| 221 """ | |
| 222 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 223 >>> d != OrderedDict(d) | |
| 224 False | |
| 225 >>> d != OrderedDict(((1, 3), (2, 1), (3, 2))) | |
| 226 True | |
| 227 >>> d != OrderedDict(((1, 0), (3, 2), (2, 1))) | |
| 228 True | |
| 229 >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) | |
| 230 False | |
| 231 >>> d != dict(d) | |
| 232 True | |
| 233 >>> d != False | |
| 234 True | |
| 235 """ | |
| 236 if isinstance(other, OrderedDict): | |
| 237 # FIXME: efficiency? | |
| 238 # Generate both item lists for each compare | |
| 239 return not (self.items() == other.items()) | |
| 240 else: | |
| 241 return True | |
| 242 | |
| 243 def __gt__(self, other): | |
| 244 """ | |
| 245 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 246 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) | |
| 247 >>> d > c | |
| 248 True | |
| 249 >>> c > d | |
| 250 False | |
| 251 >>> d > dict(c) | |
| 252 Traceback (most recent call last): | |
| 253 TypeError: Can only compare with other OrderedDicts | |
| 254 """ | |
| 255 if not isinstance(other, OrderedDict): | |
| 256 raise TypeError('Can only compare with other OrderedDicts') | |
| 257 # FIXME: efficiency? | |
| 258 # Generate both item lists for each compare | |
| 259 return (self.items() > other.items()) | |
| 260 | |
| 261 def __ge__(self, other): | |
| 262 """ | |
| 263 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 264 >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) | |
| 265 >>> e = OrderedDict(d) | |
| 266 >>> c >= d | |
| 267 False | |
| 268 >>> d >= c | |
| 269 True | |
| 270 >>> d >= dict(c) | |
| 271 Traceback (most recent call last): | |
| 272 TypeError: Can only compare with other OrderedDicts | |
| 273 >>> e >= d | |
| 274 True | |
| 275 """ | |
| 276 if not isinstance(other, OrderedDict): | |
| 277 raise TypeError('Can only compare with other OrderedDicts') | |
| 278 # FIXME: efficiency? | |
| 279 # Generate both item lists for each compare | |
| 280 return (self.items() >= other.items()) | |
| 281 | |
| 282 def __repr__(self): | |
| 283 """ | |
| 284 Used for __repr__ and __str__ | |
| 285 | |
| 286 >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) | |
| 287 >>> r1 | |
| 288 "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])" | |
| 289 >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) | |
| 290 >>> r2 | |
| 291 "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])" | |
| 292 >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) | |
| 293 True | |
| 294 >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) | |
| 295 True | |
| 296 """ | |
| 297 return '%s([%s])' % (self.__class__.__name__, ', '.join( | |
| 298 ['(%r, %r)' % (key, self[key]) for key in self._sequence])) | |
| 299 | |
| 300 def __setitem__(self, key, val): | |
| 301 """ | |
| 302 Allows slice assignment, so long as the slice is an OrderedDict | |
| 303 >>> d = OrderedDict() | |
| 304 >>> d['a'] = 'b' | |
| 305 >>> d['b'] = 'a' | |
| 306 >>> d[3] = 12 | |
| 307 >>> d | |
| 308 OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)]) | |
| 309 >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4))) | |
| 310 >>> d | |
| 311 OrderedDict([(1, 2), (2, 3), (3, 4)]) | |
| 312 >>> d[::2] = OrderedDict(((7, 8), (9, 10))) | |
| 313 >>> d | |
| 314 OrderedDict([(7, 8), (2, 3), (9, 10)]) | |
| 315 >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4))) | |
| 316 >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) | |
| 317 >>> d | |
| 318 OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) | |
| 319 >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True) | |
| 320 >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) | |
| 321 >>> d | |
| 322 OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) | |
| 323 | |
| 324 >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True) | |
| 325 >>> a[3] = 4 | |
| 326 >>> a | |
| 327 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 328 >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 329 >>> a | |
| 330 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 331 >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]) | |
| 332 Traceback (most recent call last): | |
| 333 ValueError: slice assignment must be from unique keys | |
| 334 >>> a = OrderedDict(((0, 1), (1, 2), (2, 3))) | |
| 335 >>> a[3] = 4 | |
| 336 >>> a | |
| 337 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 338 >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 339 >>> a | |
| 340 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 341 >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 342 >>> a | |
| 343 OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 344 >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 345 >>> a | |
| 346 OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)]) | |
| 347 | |
| 348 >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 349 >>> d[:1] = 3 | |
| 350 Traceback (most recent call last): | |
| 351 TypeError: slice assignment requires an OrderedDict | |
| 352 | |
| 353 >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) | |
| 354 >>> d[:1] = OrderedDict([(9, 8)]) | |
| 355 >>> d | |
| 356 OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)]) | |
| 357 """ | |
| 358 if isinstance(key, types.SliceType): | |
| 359 if not isinstance(val, OrderedDict): | |
| 360 # FIXME: allow a list of tuples? | |
| 361 raise TypeError('slice assignment requires an OrderedDict') | |
| 362 keys = self._sequence[key] | |
| 363 # NOTE: Could use ``range(*key.indices(len(self._sequence)))`` | |
| 364 indexes = range(len(self._sequence))[key] | |
| 365 if key.step is None: | |
| 366 # NOTE: new slice may not be the same size as the one being | |
| 367 # overwritten ! | |
| 368 # NOTE: What is the algorithm for an impossible slice? | |
| 369 # e.g. d[5:3] | |
| 370 pos = key.start or 0 | |
| 371 del self[key] | |
| 372 newkeys = val.keys() | |
| 373 for k in newkeys: | |
| 374 if k in self: | |
| 375 if self.strict: | |
| 376 raise ValueError('slice assignment must be from ' | |
| 377 'unique keys') | |
| 378 else: | |
| 379 # NOTE: This removes duplicate keys *first* | |
| 380 # so start position might have changed? | |
| 381 del self[k] | |
| 382 self._sequence = (self._sequence[:pos] + newkeys + | |
| 383 self._sequence[pos:]) | |
| 384 dict.update(self, val) | |
| 385 else: | |
| 386 # extended slice - length of new slice must be the same | |
| 387 # as the one being replaced | |
| 388 if len(keys) != len(val): | |
| 389 raise ValueError('attempt to assign sequence of size %s ' | |
| 390 'to extended slice of size %s' % (len(val), len(keys))) | |
| 391 # FIXME: efficiency? | |
| 392 del self[key] | |
| 393 item_list = zip(indexes, val.items()) | |
| 394 # smallest indexes first - higher indexes not guaranteed to | |
| 395 # exist | |
| 396 item_list.sort() | |
| 397 for pos, (newkey, newval) in item_list: | |
| 398 if self.strict and newkey in self: | |
| 399 raise ValueError('slice assignment must be from unique' | |
| 400 ' keys') | |
| 401 self.insert(pos, newkey, newval) | |
| 402 else: | |
| 403 if key not in self: | |
| 404 self._sequence.append(key) | |
| 405 dict.__setitem__(self, key, val) | |
| 406 | |
| 407 def __getitem__(self, key): | |
| 408 """ | |
| 409 Allows slicing. Returns an OrderedDict if you slice. | |
| 410 >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)]) | |
| 411 >>> b[::-1] | |
| 412 OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)]) | |
| 413 >>> b[2:5] | |
| 414 OrderedDict([(5, 2), (4, 3), (3, 4)]) | |
| 415 >>> type(b[2:4]) | |
| 416 <class '__main__.OrderedDict'> | |
| 417 """ | |
| 418 if isinstance(key, types.SliceType): | |
| 419 # FIXME: does this raise the error we want? | |
| 420 keys = self._sequence[key] | |
| 421 # FIXME: efficiency? | |
| 422 return OrderedDict([(entry, self[entry]) for entry in keys]) | |
| 423 else: | |
| 424 return dict.__getitem__(self, key) | |
| 425 | |
| 426 __str__ = __repr__ | |
| 427 | |
| 428 def __setattr__(self, name, value): | |
| 429 """ | |
| 430 Implemented so that accesses to ``sequence`` raise a warning and are | |
| 431 diverted to the new ``setkeys`` method. | |
| 432 """ | |
| 433 if name == 'sequence': | |
| 434 warnings.warn('Use of the sequence attribute is deprecated.' | |
| 435 ' Use the keys method instead.', DeprecationWarning) | |
| 436 # NOTE: doesn't return anything | |
| 437 self.setkeys(value) | |
| 438 else: | |
| 439 # FIXME: do we want to allow arbitrary setting of attributes? | |
| 440 # Or do we want to manage it? | |
| 441 object.__setattr__(self, name, value) | |
| 442 | |
| 443 def __getattr__(self, name): | |
| 444 """ | |
| 445 Implemented so that access to ``sequence`` raises a warning. | |
| 446 | |
| 447 >>> d = OrderedDict() | |
| 448 >>> d.sequence | |
| 449 [] | |
| 450 """ | |
| 451 if name == 'sequence': | |
| 452 warnings.warn('Use of the sequence attribute is deprecated.' | |
| 453 ' Use the keys method instead.', DeprecationWarning) | |
| 454 # NOTE: Still (currently) returns a direct reference. Need to | |
| 455 # because code that uses sequence will expect to be able to | |
| 456 # mutate it in place. | |
| 457 return self._sequence | |
| 458 else: | |
| 459 # raise the appropriate error | |
| 460 raise AttributeError("OrderedDict has no '%s' attribute" % name) | |
| 461 | |
| 462 def __deepcopy__(self, memo): | |
| 463 """ | |
| 464 To allow deepcopy to work with OrderedDict. | |
| 465 | |
| 466 >>> from copy import deepcopy | |
| 467 >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)]) | |
| 468 >>> a['test'] = {} | |
| 469 >>> b = deepcopy(a) | |
| 470 >>> b == a | |
| 471 True | |
| 472 >>> b is a | |
| 473 False | |
| 474 >>> a['test'] is b['test'] | |
| 475 False | |
| 476 """ | |
| 477 from copy import deepcopy | |
| 478 return self.__class__(deepcopy(self.items(), memo), self.strict) | |
| 479 | |
| 480 | |
| 481 ### Read-only methods ### | |
| 482 | |
| 483 def copy(self): | |
| 484 """ | |
| 485 >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy() | |
| 486 OrderedDict([(1, 3), (3, 2), (2, 1)]) | |
| 487 """ | |
| 488 return OrderedDict(self) | |
| 489 | |
| 490 def items(self): | |
| 491 """ | |
| 492 ``items`` returns a list of tuples representing all the | |
| 493 ``(key, value)`` pairs in the dictionary. | |
| 494 | |
| 495 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 496 >>> d.items() | |
| 497 [(1, 3), (3, 2), (2, 1)] | |
| 498 >>> d.clear() | |
| 499 >>> d.items() | |
| 500 [] | |
| 501 """ | |
| 502 return zip(self._sequence, self.values()) | |
| 503 | |
| 504 def keys(self): | |
| 505 """ | |
| 506 Return a list of keys in the ``OrderedDict``. | |
| 507 | |
| 508 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 509 >>> d.keys() | |
| 510 [1, 3, 2] | |
| 511 """ | |
| 512 return self._sequence[:] | |
| 513 | |
| 514 def values(self, values=None): | |
| 515 """ | |
| 516 Return a list of all the values in the OrderedDict. | |
| 517 | |
| 518 Optionally you can pass in a list of values, which will replace the | |
| 519 current list. The value list must be the same len as the OrderedDict. | |
| 520 | |
| 521 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 522 >>> d.values() | |
| 523 [3, 2, 1] | |
| 524 """ | |
| 525 return [self[key] for key in self._sequence] | |
| 526 | |
| 527 def iteritems(self): | |
| 528 """ | |
| 529 >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems() | |
| 530 >>> ii.next() | |
| 531 (1, 3) | |
| 532 >>> ii.next() | |
| 533 (3, 2) | |
| 534 >>> ii.next() | |
| 535 (2, 1) | |
| 536 >>> ii.next() | |
| 537 Traceback (most recent call last): | |
| 538 StopIteration | |
| 539 """ | |
| 540 def make_iter(self=self): | |
| 541 keys = self.iterkeys() | |
| 542 while True: | |
| 543 key = keys.next() | |
| 544 yield (key, self[key]) | |
| 545 return make_iter() | |
| 546 | |
| 547 def iterkeys(self): | |
| 548 """ | |
| 549 >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys() | |
| 550 >>> ii.next() | |
| 551 1 | |
| 552 >>> ii.next() | |
| 553 3 | |
| 554 >>> ii.next() | |
| 555 2 | |
| 556 >>> ii.next() | |
| 557 Traceback (most recent call last): | |
| 558 StopIteration | |
| 559 """ | |
| 560 return iter(self._sequence) | |
| 561 | |
| 562 __iter__ = iterkeys | |
| 563 | |
| 564 def itervalues(self): | |
| 565 """ | |
| 566 >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues() | |
| 567 >>> iv.next() | |
| 568 3 | |
| 569 >>> iv.next() | |
| 570 2 | |
| 571 >>> iv.next() | |
| 572 1 | |
| 573 >>> iv.next() | |
| 574 Traceback (most recent call last): | |
| 575 StopIteration | |
| 576 """ | |
| 577 def make_iter(self=self): | |
| 578 keys = self.iterkeys() | |
| 579 while True: | |
| 580 yield self[keys.next()] | |
| 581 return make_iter() | |
| 582 | |
| 583 ### Read-write methods ### | |
| 584 | |
| 585 def clear(self): | |
| 586 """ | |
| 587 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 588 >>> d.clear() | |
| 589 >>> d | |
| 590 OrderedDict([]) | |
| 591 """ | |
| 592 dict.clear(self) | |
| 593 self._sequence = [] | |
| 594 | |
| 595 def pop(self, key, *args): | |
| 596 """ | |
| 597 No dict.pop in Python 2.2, gotta reimplement it | |
| 598 | |
| 599 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 600 >>> d.pop(3) | |
| 601 2 | |
| 602 >>> d | |
| 603 OrderedDict([(1, 3), (2, 1)]) | |
| 604 >>> d.pop(4) | |
| 605 Traceback (most recent call last): | |
| 606 KeyError: 4 | |
| 607 >>> d.pop(4, 0) | |
| 608 0 | |
| 609 >>> d.pop(4, 0, 1) | |
| 610 Traceback (most recent call last): | |
| 611 TypeError: pop expected at most 2 arguments, got 3 | |
| 612 """ | |
| 613 if len(args) > 1: | |
| 614 raise TypeError, ('pop expected at most 2 arguments, got %s' % | |
| 615 (len(args) + 1)) | |
| 616 if key in self: | |
| 617 val = self[key] | |
| 618 del self[key] | |
| 619 else: | |
| 620 try: | |
| 621 val = args[0] | |
| 622 except IndexError: | |
| 623 raise KeyError(key) | |
| 624 return val | |
| 625 | |
| 626 def popitem(self, i=-1): | |
| 627 """ | |
| 628 Delete and return an item specified by index, not a random one as in | |
| 629 dict. The index is -1 by default (the last item). | |
| 630 | |
| 631 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 632 >>> d.popitem() | |
| 633 (2, 1) | |
| 634 >>> d | |
| 635 OrderedDict([(1, 3), (3, 2)]) | |
| 636 >>> d.popitem(0) | |
| 637 (1, 3) | |
| 638 >>> OrderedDict().popitem() | |
| 639 Traceback (most recent call last): | |
| 640 KeyError: 'popitem(): dictionary is empty' | |
| 641 >>> d.popitem(2) | |
| 642 Traceback (most recent call last): | |
| 643 IndexError: popitem(): index 2 not valid | |
| 644 """ | |
| 645 if not self._sequence: | |
| 646 raise KeyError('popitem(): dictionary is empty') | |
| 647 try: | |
| 648 key = self._sequence[i] | |
| 649 except IndexError: | |
| 650 raise IndexError('popitem(): index %s not valid' % i) | |
| 651 return (key, self.pop(key)) | |
| 652 | |
| 653 def setdefault(self, key, defval = None): | |
| 654 """ | |
| 655 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 656 >>> d.setdefault(1) | |
| 657 3 | |
| 658 >>> d.setdefault(4) is None | |
| 659 True | |
| 660 >>> d | |
| 661 OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)]) | |
| 662 >>> d.setdefault(5, 0) | |
| 663 0 | |
| 664 >>> d | |
| 665 OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)]) | |
| 666 """ | |
| 667 if key in self: | |
| 668 return self[key] | |
| 669 else: | |
| 670 self[key] = defval | |
| 671 return defval | |
| 672 | |
| 673 def update(self, from_od): | |
| 674 """ | |
| 675 Update from another OrderedDict or sequence of (key, value) pairs | |
| 676 | |
| 677 >>> d = OrderedDict(((1, 0), (0, 1))) | |
| 678 >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1)))) | |
| 679 >>> d | |
| 680 OrderedDict([(1, 3), (0, 1), (3, 2), (2, 1)]) | |
| 681 >>> d.update({4: 4}) | |
| 682 Traceback (most recent call last): | |
| 683 TypeError: undefined order, cannot get items from dict | |
| 684 >>> d.update((4, 4)) | |
| 685 Traceback (most recent call last): | |
| 686 TypeError: cannot convert dictionary update sequence element "4" to a 2-item sequence | |
| 687 """ | |
| 688 if isinstance(from_od, OrderedDict): | |
| 689 for key, val in from_od.items(): | |
| 690 self[key] = val | |
| 691 elif isinstance(from_od, dict): | |
| 692 # we lose compatibility with other ordered dict types this way | |
| 693 raise TypeError('undefined order, cannot get items from dict') | |
| 694 else: | |
| 695 # FIXME: efficiency? | |
| 696 # sequence of 2-item sequences, or error | |
| 697 for item in from_od: | |
| 698 try: | |
| 699 key, val = item | |
| 700 except TypeError: | |
| 701 raise TypeError('cannot convert dictionary update' | |
| 702 ' sequence element "%s" to a 2-item sequence' % item) | |
| 703 self[key] = val | |
| 704 | |
| 705 def rename(self, old_key, new_key): | |
| 706 """ | |
| 707 Rename the key for a given value, without modifying sequence order. | |
| 708 | |
| 709 For the case where new_key already exists this raise an exception, | |
| 710 since if new_key exists, it is ambiguous as to what happens to the | |
| 711 associated values, and the position of new_key in the sequence. | |
| 712 | |
| 713 >>> od = OrderedDict() | |
| 714 >>> od['a'] = 1 | |
| 715 >>> od['b'] = 2 | |
| 716 >>> od.items() | |
| 717 [('a', 1), ('b', 2)] | |
| 718 >>> od.rename('b', 'c') | |
| 719 >>> od.items() | |
| 720 [('a', 1), ('c', 2)] | |
| 721 >>> od.rename('c', 'a') | |
| 722 Traceback (most recent call last): | |
| 723 ValueError: New key already exists: 'a' | |
| 724 >>> od.rename('d', 'b') | |
| 725 Traceback (most recent call last): | |
| 726 KeyError: 'd' | |
| 727 """ | |
| 728 if new_key == old_key: | |
| 729 # no-op | |
| 730 return | |
| 731 if new_key in self: | |
| 732 raise ValueError("New key already exists: %r" % new_key) | |
| 733 # rename sequence entry | |
| 734 value = self[old_key] | |
| 735 old_idx = self._sequence.index(old_key) | |
| 736 self._sequence[old_idx] = new_key | |
| 737 # rename internal dict entry | |
| 738 dict.__delitem__(self, old_key) | |
| 739 dict.__setitem__(self, new_key, value) | |
| 740 | |
| 741 def setitems(self, items): | |
| 742 """ | |
| 743 This method allows you to set the items in the dict. | |
| 744 | |
| 745 It takes a list of tuples - of the same sort returned by the ``items`` | |
| 746 method. | |
| 747 | |
| 748 >>> d = OrderedDict() | |
| 749 >>> d.setitems(((3, 1), (2, 3), (1, 2))) | |
| 750 >>> d | |
| 751 OrderedDict([(3, 1), (2, 3), (1, 2)]) | |
| 752 """ | |
| 753 self.clear() | |
| 754 # FIXME: this allows you to pass in an OrderedDict as well :-) | |
| 755 self.update(items) | |
| 756 | |
| 757 def setkeys(self, keys): | |
| 758 """ | |
| 759 ``setkeys`` all ows you to pass in a new list of keys which will | |
| 760 replace the current set. This must contain the same set of keys, but | |
| 761 need not be in the same order. | |
| 762 | |
| 763 If you pass in new keys that don't match, a ``KeyError`` will be | |
| 764 raised. | |
| 765 | |
| 766 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 767 >>> d.keys() | |
| 768 [1, 3, 2] | |
| 769 >>> d.setkeys((1, 2, 3)) | |
| 770 >>> d | |
| 771 OrderedDict([(1, 3), (2, 1), (3, 2)]) | |
| 772 >>> d.setkeys(['a', 'b', 'c']) | |
| 773 Traceback (most recent call last): | |
| 774 KeyError: 'Keylist is not the same as current keylist.' | |
| 775 """ | |
| 776 # FIXME: Efficiency? (use set for Python 2.4 :-) | |
| 777 # NOTE: list(keys) rather than keys[:] because keys[:] returns | |
| 778 # a tuple, if keys is a tuple. | |
| 779 kcopy = list(keys) | |
| 780 kcopy.sort() | |
| 781 self._sequence.sort() | |
| 782 if kcopy != self._sequence: | |
| 783 raise KeyError('Keylist is not the same as current keylist.') | |
| 784 # NOTE: This makes the _sequence attribute a new object, instead | |
| 785 # of changing it in place. | |
| 786 # FIXME: efficiency? | |
| 787 self._sequence = list(keys) | |
| 788 | |
| 789 def setvalues(self, values): | |
| 790 """ | |
| 791 You can pass in a list of values, which will replace the | |
| 792 current list. The value list must be the same len as the OrderedDict. | |
| 793 | |
| 794 (Or a ``ValueError`` is raised.) | |
| 795 | |
| 796 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 797 >>> d.setvalues((1, 2, 3)) | |
| 798 >>> d | |
| 799 OrderedDict([(1, 1), (3, 2), (2, 3)]) | |
| 800 >>> d.setvalues([6]) | |
| 801 Traceback (most recent call last): | |
| 802 ValueError: Value list is not the same length as the OrderedDict. | |
| 803 """ | |
| 804 if len(values) != len(self): | |
| 805 # FIXME: correct error to raise? | |
| 806 raise ValueError('Value list is not the same length as the ' | |
| 807 'OrderedDict.') | |
| 808 self.update(zip(self, values)) | |
| 809 | |
| 810 ### Sequence Methods ### | |
| 811 | |
| 812 def index(self, key): | |
| 813 """ | |
| 814 Return the position of the specified key in the OrderedDict. | |
| 815 | |
| 816 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 817 >>> d.index(3) | |
| 818 1 | |
| 819 >>> d.index(4) | |
| 820 Traceback (most recent call last): | |
| 821 ValueError: list.index(x): x not in list | |
| 822 """ | |
| 823 return self._sequence.index(key) | |
| 824 | |
| 825 def insert(self, index, key, value): | |
| 826 """ | |
| 827 Takes ``index``, ``key``, and ``value`` as arguments. | |
| 828 | |
| 829 Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in | |
| 830 the OrderedDict. | |
| 831 | |
| 832 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 833 >>> d.insert(0, 4, 0) | |
| 834 >>> d | |
| 835 OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)]) | |
| 836 >>> d.insert(0, 2, 1) | |
| 837 >>> d | |
| 838 OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)]) | |
| 839 >>> d.insert(8, 8, 1) | |
| 840 >>> d | |
| 841 OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)]) | |
| 842 """ | |
| 843 if key in self: | |
| 844 # FIXME: efficiency? | |
| 845 del self[key] | |
| 846 self._sequence.insert(index, key) | |
| 847 dict.__setitem__(self, key, value) | |
| 848 | |
| 849 def reverse(self): | |
| 850 """ | |
| 851 Reverse the order of the OrderedDict. | |
| 852 | |
| 853 >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) | |
| 854 >>> d.reverse() | |
| 855 >>> d | |
| 856 OrderedDict([(2, 1), (3, 2), (1, 3)]) | |
| 857 """ | |
| 858 self._sequence.reverse() | |
| 859 | |
| 860 def sort(self, *args, **kwargs): | |
| 861 """ | |
| 862 Sort the key order in the OrderedDict. | |
| 863 | |
| 864 This method takes the same arguments as the ``list.sort`` method on | |
| 865 your version of Python. | |
| 866 | |
| 867 >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4))) | |
| 868 >>> d.sort() | |
| 869 >>> d | |
| 870 OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)]) | |
| 871 """ | |
| 872 self._sequence.sort(*args, **kwargs) | |
| 873 | |
| 874 class Keys(object): | |
| 875 # FIXME: should this object be a subclass of list? | |
| 876 """ | |
| 877 Custom object for accessing the keys of an OrderedDict. | |
| 878 | |
| 879 Can be called like the normal ``OrderedDict.keys`` method, but also | |
| 880 supports indexing and sequence methods. | |
| 881 """ | |
| 882 | |
| 883 def __init__(self, main): | |
| 884 self._main = main | |
| 885 | |
| 886 def __call__(self): | |
| 887 """Pretend to be the keys method.""" | |
| 888 return self._main._keys() | |
| 889 | |
| 890 def __getitem__(self, index): | |
| 891 """Fetch the key at position i.""" | |
| 892 # NOTE: this automatically supports slicing :-) | |
| 893 return self._main._sequence[index] | |
| 894 | |
| 895 def __setitem__(self, index, name): | |
| 896 """ | |
| 897 You cannot assign to keys, but you can do slice assignment to re-order | |
| 898 them. | |
| 899 | |
| 900 You can only do slice assignment if the new set of keys is a reordering | |
| 901 of the original set. | |
| 902 """ | |
| 903 if isinstance(index, types.SliceType): | |
| 904 # FIXME: efficiency? | |
| 905 # check length is the same | |
| 906 indexes = range(len(self._main._sequence))[index] | |
| 907 if len(indexes) != len(name): | |
| 908 raise ValueError('attempt to assign sequence of size %s ' | |
| 909 'to slice of size %s' % (len(name), len(indexes))) | |
| 910 # check they are the same keys | |
| 911 # FIXME: Use set | |
| 912 old_keys = self._main._sequence[index] | |
| 913 new_keys = list(name) | |
| 914 old_keys.sort() | |
| 915 new_keys.sort() | |
| 916 if old_keys != new_keys: | |
| 917 raise KeyError('Keylist is not the same as current keylist.') | |
| 918 orig_vals = [self._main[k] for k in name] | |
| 919 del self._main[index] | |
| 920 vals = zip(indexes, name, orig_vals) | |
| 921 vals.sort() | |
| 922 for i, k, v in vals: | |
| 923 if self._main.strict and k in self._main: | |
| 924 raise ValueError('slice assignment must be from ' | |
| 925 'unique keys') | |
| 926 self._main.insert(i, k, v) | |
| 927 else: | |
| 928 raise ValueError('Cannot assign to keys') | |
| 929 | |
| 930 ### following methods pinched from UserList and adapted ### | |
| 931 def __repr__(self): return repr(self._main._sequence) | |
| 932 | |
| 933 # FIXME: do we need to check if we are comparing with another ``Keys`` | |
| 934 # object? (like the __cast method of UserList) | |
| 935 def __lt__(self, other): return self._main._sequence < other | |
| 936 def __le__(self, other): return self._main._sequence <= other | |
| 937 def __eq__(self, other): return self._main._sequence == other | |
| 938 def __ne__(self, other): return self._main._sequence != other | |
| 939 def __gt__(self, other): return self._main._sequence > other | |
| 940 def __ge__(self, other): return self._main._sequence >= other | |
| 941 # FIXME: do we need __cmp__ as well as rich comparisons? | |
| 942 def __cmp__(self, other): return cmp(self._main._sequence, other) | |
| 943 | |
| 944 def __contains__(self, item): return item in self._main._sequence | |
| 945 def __len__(self): return len(self._main._sequence) | |
| 946 def __iter__(self): return self._main.iterkeys() | |
| 947 def count(self, item): return self._main._sequence.count(item) | |
| 948 def index(self, item, *args): return self._main._sequence.index(item, *args) | |
| 949 def reverse(self): self._main._sequence.reverse() | |
| 950 def sort(self, *args, **kwds): self._main._sequence.sort(*args, **kwds) | |
| 951 def __mul__(self, n): return self._main._sequence*n | |
| 952 __rmul__ = __mul__ | |
| 953 def __add__(self, other): return self._main._sequence + other | |
| 954 def __radd__(self, other): return other + self._main._sequence | |
| 955 | |
| 956 ## following methods not implemented for keys ## | |
| 957 def __delitem__(self, i): raise TypeError('Can\'t delete items from keys') | |
| 958 def __iadd__(self, other): raise TypeError('Can\'t add in place to keys') | |
| 959 def __imul__(self, n): raise TypeError('Can\'t multiply keys in place') | |
| 960 def append(self, item): raise TypeError('Can\'t append items to keys') | |
| 961 def insert(self, i, item): raise TypeError('Can\'t insert items into keys') | |
| 962 def pop(self, i=-1): raise TypeError('Can\'t pop items from keys') | |
| 963 def remove(self, item): raise TypeError('Can\'t remove items from keys') | |
| 964 def extend(self, other): raise TypeError('Can\'t extend keys') | |
| 965 | |
| 966 class Items(object): | |
| 967 """ | |
| 968 Custom object for accessing the items of an OrderedDict. | |
| 969 | |
| 970 Can be called like the normal ``OrderedDict.items`` method, but also | |
| 971 supports indexing and sequence methods. | |
| 972 """ | |
| 973 | |
| 974 def __init__(self, main): | |
| 975 self._main = main | |
| 976 | |
| 977 def __call__(self): | |
| 978 """Pretend to be the items method.""" | |
| 979 return self._main._items() | |
| 980 | |
| 981 def __getitem__(self, index): | |
| 982 """Fetch the item at position i.""" | |
| 983 if isinstance(index, types.SliceType): | |
| 984 # fetching a slice returns an OrderedDict | |
| 985 return self._main[index].items() | |
| 986 key = self._main._sequence[index] | |
| 987 return (key, self._main[key]) | |
| 988 | |
| 989 def __setitem__(self, index, item): | |
| 990 """Set item at position i to item.""" | |
| 991 if isinstance(index, types.SliceType): | |
| 992 # NOTE: item must be an iterable (list of tuples) | |
| 993 self._main[index] = OrderedDict(item) | |
| 994 else: | |
| 995 # FIXME: Does this raise a sensible error? | |
| 996 orig = self._main.keys[index] | |
| 997 key, value = item | |
| 998 if self._main.strict and key in self and (key != orig): | |
| 999 raise ValueError('slice assignment must be from ' | |
| 1000 'unique keys') | |
| 1001 # delete the current one | |
| 1002 del self._main[self._main._sequence[index]] | |
| 1003 self._main.insert(index, key, value) | |
| 1004 | |
| 1005 def __delitem__(self, i): | |
| 1006 """Delete the item at position i.""" | |
| 1007 key = self._main._sequence[i] | |
| 1008 if isinstance(i, types.SliceType): | |
| 1009 for k in key: | |
| 1010 # FIXME: efficiency? | |
| 1011 del self._main[k] | |
| 1012 else: | |
| 1013 del self._main[key] | |
| 1014 | |
| 1015 ### following methods pinched from UserList and adapted ### | |
| 1016 def __repr__(self): return repr(self._main.items()) | |
| 1017 | |
| 1018 # FIXME: do we need to check if we are comparing with another ``Items`` | |
| 1019 # object? (like the __cast method of UserList) | |
| 1020 def __lt__(self, other): return self._main.items() < other | |
| 1021 def __le__(self, other): return self._main.items() <= other | |
| 1022 def __eq__(self, other): return self._main.items() == other | |
| 1023 def __ne__(self, other): return self._main.items() != other | |
| 1024 def __gt__(self, other): return self._main.items() > other | |
| 1025 def __ge__(self, other): return self._main.items() >= other | |
| 1026 def __cmp__(self, other): return cmp(self._main.items(), other) | |
| 1027 | |
| 1028 def __contains__(self, item): return item in self._main.items() | |
| 1029 def __len__(self): return len(self._main._sequence) # easier :-) | |
| 1030 def __iter__(self): return self._main.iteritems() | |
| 1031 def count(self, item): return self._main.items().count(item) | |
| 1032 def index(self, item, *args): return self._main.items().index(item, *args) | |
| 1033 def reverse(self): self._main.reverse() | |
| 1034 def sort(self, *args, **kwds): self._main.sort(*args, **kwds) | |
| 1035 def __mul__(self, n): return self._main.items()*n | |
| 1036 __rmul__ = __mul__ | |
| 1037 def __add__(self, other): return self._main.items() + other | |
| 1038 def __radd__(self, other): return other + self._main.items() | |
| 1039 | |
| 1040 def append(self, item): | |
| 1041 """Add an item to the end.""" | |
| 1042 # FIXME: this is only append if the key isn't already present | |
| 1043 key, value = item | |
| 1044 self._main[key] = value | |
| 1045 | |
| 1046 def insert(self, i, item): | |
| 1047 key, value = item | |
| 1048 self._main.insert(i, key, value) | |
| 1049 | |
| 1050 def pop(self, i=-1): | |
| 1051 key = self._main._sequence[i] | |
| 1052 return (key, self._main.pop(key)) | |
| 1053 | |
| 1054 def remove(self, item): | |
| 1055 key, value = item | |
| 1056 try: | |
| 1057 assert value == self._main[key] | |
| 1058 except (KeyError, AssertionError): | |
| 1059 raise ValueError('ValueError: list.remove(x): x not in list') | |
| 1060 else: | |
| 1061 del self._main[key] | |
| 1062 | |
| 1063 def extend(self, other): | |
| 1064 # FIXME: is only a true extend if none of the keys already present | |
| 1065 for item in other: | |
| 1066 key, value = item | |
| 1067 self._main[key] = value | |
| 1068 | |
| 1069 def __iadd__(self, other): | |
| 1070 self.extend(other) | |
| 1071 | |
| 1072 ## following methods not implemented for items ## | |
| 1073 | |
| 1074 def __imul__(self, n): raise TypeError('Can\'t multiply items in place') | |
| 1075 | |
| 1076 class Values(object): | |
| 1077 """ | |
| 1078 Custom object for accessing the values of an OrderedDict. | |
| 1079 | |
| 1080 Can be called like the normal ``OrderedDict.values`` method, but also | |
| 1081 supports indexing and sequence methods. | |
| 1082 """ | |
| 1083 | |
| 1084 def __init__(self, main): | |
| 1085 self._main = main | |
| 1086 | |
| 1087 def __call__(self): | |
| 1088 """Pretend to be the values method.""" | |
| 1089 return self._main._values() | |
| 1090 | |
| 1091 def __getitem__(self, index): | |
| 1092 """Fetch the value at position i.""" | |
| 1093 if isinstance(index, types.SliceType): | |
| 1094 return [self._main[key] for key in self._main._sequence[index]] | |
| 1095 else: | |
| 1096 return self._main[self._main._sequence[index]] | |
| 1097 | |
| 1098 def __setitem__(self, index, value): | |
| 1099 """ | |
| 1100 Set the value at position i to value. | |
| 1101 | |
| 1102 You can only do slice assignment to values if you supply a sequence of | |
| 1103 equal length to the slice you are replacing. | |
| 1104 """ | |
| 1105 if isinstance(index, types.SliceType): | |
| 1106 keys = self._main._sequence[index] | |
| 1107 if len(keys) != len(value): | |
| 1108 raise ValueError('attempt to assign sequence of size %s ' | |
| 1109 'to slice of size %s' % (len(name), len(keys))) | |
| 1110 # FIXME: efficiency? Would be better to calculate the indexes | |
| 1111 # directly from the slice object | |
| 1112 # NOTE: the new keys can collide with existing keys (or even | |
| 1113 # contain duplicates) - these will overwrite | |
| 1114 for key, val in zip(keys, value): | |
| 1115 self._main[key] = val | |
| 1116 else: | |
| 1117 self._main[self._main._sequence[index]] = value | |
| 1118 | |
| 1119 ### following methods pinched from UserList and adapted ### | |
| 1120 def __repr__(self): return repr(self._main.values()) | |
| 1121 | |
| 1122 # FIXME: do we need to check if we are comparing with another ``Values`` | |
| 1123 # object? (like the __cast method of UserList) | |
| 1124 def __lt__(self, other): return self._main.values() < other | |
| 1125 def __le__(self, other): return self._main.values() <= other | |
| 1126 def __eq__(self, other): return self._main.values() == other | |
| 1127 def __ne__(self, other): return self._main.values() != other | |
| 1128 def __gt__(self, other): return self._main.values() > other | |
| 1129 def __ge__(self, other): return self._main.values() >= other | |
| 1130 def __cmp__(self, other): return cmp(self._main.values(), other) | |
| 1131 | |
| 1132 def __contains__(self, item): return item in self._main.values() | |
| 1133 def __len__(self): return len(self._main._sequence) # easier :-) | |
| 1134 def __iter__(self): return self._main.itervalues() | |
| 1135 def count(self, item): return self._main.values().count(item) | |
| 1136 def index(self, item, *args): return self._main.values().index(item, *args) | |
| 1137 | |
| 1138 def reverse(self): | |
| 1139 """Reverse the values""" | |
| 1140 vals = self._main.values() | |
| 1141 vals.reverse() | |
| 1142 # FIXME: efficiency | |
| 1143 self[:] = vals | |
| 1144 | |
| 1145 def sort(self, *args, **kwds): | |
| 1146 """Sort the values.""" | |
| 1147 vals = self._main.values() | |
| 1148 vals.sort(*args, **kwds) | |
| 1149 self[:] = vals | |
| 1150 | |
| 1151 def __mul__(self, n): return self._main.values()*n | |
| 1152 __rmul__ = __mul__ | |
| 1153 def __add__(self, other): return self._main.values() + other | |
| 1154 def __radd__(self, other): return other + self._main.values() | |
| 1155 | |
| 1156 ## following methods not implemented for values ## | |
| 1157 def __delitem__(self, i): raise TypeError('Can\'t delete items from values') | |
| 1158 def __iadd__(self, other): raise TypeError('Can\'t add in place to values') | |
| 1159 def __imul__(self, n): raise TypeError('Can\'t multiply values in place') | |
| 1160 def append(self, item): raise TypeError('Can\'t append items to values') | |
| 1161 def insert(self, i, item): raise TypeError('Can\'t insert items into values') | |
| 1162 def pop(self, i=-1): raise TypeError('Can\'t pop items from values') | |
| 1163 def remove(self, item): raise TypeError('Can\'t remove items from values') | |
| 1164 def extend(self, other): raise TypeError('Can\'t extend values') | |
| 1165 | |
| 1166 class SequenceOrderedDict(OrderedDict): | |
| 1167 """ | |
| 1168 Experimental version of OrderedDict that has a custom object for ``keys``, | |
| 1169 ``values``, and ``items``. | |
| 1170 | |
| 1171 These are callable sequence objects that work as methods, or can be | |
| 1172 manipulated directly as sequences. | |
| 1173 | |
| 1174 Test for ``keys``, ``items`` and ``values``. | |
| 1175 | |
| 1176 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) | |
| 1177 >>> d | |
| 1178 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) | |
| 1179 >>> d.keys | |
| 1180 [1, 2, 3] | |
| 1181 >>> d.keys() | |
| 1182 [1, 2, 3] | |
| 1183 >>> d.setkeys((3, 2, 1)) | |
| 1184 >>> d | |
| 1185 SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) | |
| 1186 >>> d.setkeys((1, 2, 3)) | |
| 1187 >>> d.keys[0] | |
| 1188 1 | |
| 1189 >>> d.keys[:] | |
| 1190 [1, 2, 3] | |
| 1191 >>> d.keys[-1] | |
| 1192 3 | |
| 1193 >>> d.keys[-2] | |
| 1194 2 | |
| 1195 >>> d.keys[0:2] = [2, 1] | |
| 1196 >>> d | |
| 1197 SequenceOrderedDict([(2, 3), (1, 2), (3, 4)]) | |
| 1198 >>> d.keys.reverse() | |
| 1199 >>> d.keys | |
| 1200 [3, 1, 2] | |
| 1201 >>> d.keys = [1, 2, 3] | |
| 1202 >>> d | |
| 1203 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) | |
| 1204 >>> d.keys = [3, 1, 2] | |
| 1205 >>> d | |
| 1206 SequenceOrderedDict([(3, 4), (1, 2), (2, 3)]) | |
| 1207 >>> a = SequenceOrderedDict() | |
| 1208 >>> b = SequenceOrderedDict() | |
| 1209 >>> a.keys == b.keys | |
| 1210 1 | |
| 1211 >>> a['a'] = 3 | |
| 1212 >>> a.keys == b.keys | |
| 1213 0 | |
| 1214 >>> b['a'] = 3 | |
| 1215 >>> a.keys == b.keys | |
| 1216 1 | |
| 1217 >>> b['b'] = 3 | |
| 1218 >>> a.keys == b.keys | |
| 1219 0 | |
| 1220 >>> a.keys > b.keys | |
| 1221 0 | |
| 1222 >>> a.keys < b.keys | |
| 1223 1 | |
| 1224 >>> 'a' in a.keys | |
| 1225 1 | |
| 1226 >>> len(b.keys) | |
| 1227 2 | |
| 1228 >>> 'c' in d.keys | |
| 1229 0 | |
| 1230 >>> 1 in d.keys | |
| 1231 1 | |
| 1232 >>> [v for v in d.keys] | |
| 1233 [3, 1, 2] | |
| 1234 >>> d.keys.sort() | |
| 1235 >>> d.keys | |
| 1236 [1, 2, 3] | |
| 1237 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True) | |
| 1238 >>> d.keys[::-1] = [1, 2, 3] | |
| 1239 >>> d | |
| 1240 SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) | |
| 1241 >>> d.keys[:2] | |
| 1242 [3, 2] | |
| 1243 >>> d.keys[:2] = [1, 3] | |
| 1244 Traceback (most recent call last): | |
| 1245 KeyError: 'Keylist is not the same as current keylist.' | |
| 1246 | |
| 1247 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) | |
| 1248 >>> d | |
| 1249 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) | |
| 1250 >>> d.values | |
| 1251 [2, 3, 4] | |
| 1252 >>> d.values() | |
| 1253 [2, 3, 4] | |
| 1254 >>> d.setvalues((4, 3, 2)) | |
| 1255 >>> d | |
| 1256 SequenceOrderedDict([(1, 4), (2, 3), (3, 2)]) | |
| 1257 >>> d.values[::-1] | |
| 1258 [2, 3, 4] | |
| 1259 >>> d.values[0] | |
| 1260 4 | |
| 1261 >>> d.values[-2] | |
| 1262 3 | |
| 1263 >>> del d.values[0] | |
| 1264 Traceback (most recent call last): | |
| 1265 TypeError: Can't delete items from values | |
| 1266 >>> d.values[::2] = [2, 4] | |
| 1267 >>> d | |
| 1268 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) | |
| 1269 >>> 7 in d.values | |
| 1270 0 | |
| 1271 >>> len(d.values) | |
| 1272 3 | |
| 1273 >>> [val for val in d.values] | |
| 1274 [2, 3, 4] | |
| 1275 >>> d.values[-1] = 2 | |
| 1276 >>> d.values.count(2) | |
| 1277 2 | |
| 1278 >>> d.values.index(2) | |
| 1279 0 | |
| 1280 >>> d.values[-1] = 7 | |
| 1281 >>> d.values | |
| 1282 [2, 3, 7] | |
| 1283 >>> d.values.reverse() | |
| 1284 >>> d.values | |
| 1285 [7, 3, 2] | |
| 1286 >>> d.values.sort() | |
| 1287 >>> d.values | |
| 1288 [2, 3, 7] | |
| 1289 >>> d.values.append('anything') | |
| 1290 Traceback (most recent call last): | |
| 1291 TypeError: Can't append items to values | |
| 1292 >>> d.values = (1, 2, 3) | |
| 1293 >>> d | |
| 1294 SequenceOrderedDict([(1, 1), (2, 2), (3, 3)]) | |
| 1295 | |
| 1296 >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4))) | |
| 1297 >>> d | |
| 1298 SequenceOrderedDict([(1, 2), (2, 3), (3, 4)]) | |
| 1299 >>> d.items() | |
| 1300 [(1, 2), (2, 3), (3, 4)] | |
| 1301 >>> d.setitems([(3, 4), (2 ,3), (1, 2)]) | |
| 1302 >>> d | |
| 1303 SequenceOrderedDict([(3, 4), (2, 3), (1, 2)]) | |
| 1304 >>> d.items[0] | |
| 1305 (3, 4) | |
| 1306 >>> d.items[:-1] | |
| 1307 [(3, 4), (2, 3)] | |
| 1308 >>> d.items[1] = (6, 3) | |
| 1309 >>> d.items | |
| 1310 [(3, 4), (6, 3), (1, 2)] | |
| 1311 >>> d.items[1:2] = [(9, 9)] | |
| 1312 >>> d | |
| 1313 SequenceOrderedDict([(3, 4), (9, 9), (1, 2)]) | |
| 1314 >>> del d.items[1:2] | |
| 1315 >>> d | |
| 1316 SequenceOrderedDict([(3, 4), (1, 2)]) | |
| 1317 >>> (3, 4) in d.items | |
| 1318 1 | |
| 1319 >>> (4, 3) in d.items | |
| 1320 0 | |
| 1321 >>> len(d.items) | |
| 1322 2 | |
| 1323 >>> [v for v in d.items] | |
| 1324 [(3, 4), (1, 2)] | |
| 1325 >>> d.items.count((3, 4)) | |
| 1326 1 | |
| 1327 >>> d.items.index((1, 2)) | |
| 1328 1 | |
| 1329 >>> d.items.index((2, 1)) | |
| 1330 Traceback (most recent call last): | |
| 1331 ValueError: list.index(x): x not in list | |
| 1332 >>> d.items.reverse() | |
| 1333 >>> d.items | |
| 1334 [(1, 2), (3, 4)] | |
| 1335 >>> d.items.reverse() | |
| 1336 >>> d.items.sort() | |
| 1337 >>> d.items | |
| 1338 [(1, 2), (3, 4)] | |
| 1339 >>> d.items.append((5, 6)) | |
| 1340 >>> d.items | |
| 1341 [(1, 2), (3, 4), (5, 6)] | |
| 1342 >>> d.items.insert(0, (0, 0)) | |
| 1343 >>> d.items | |
| 1344 [(0, 0), (1, 2), (3, 4), (5, 6)] | |
| 1345 >>> d.items.insert(-1, (7, 8)) | |
| 1346 >>> d.items | |
| 1347 [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)] | |
| 1348 >>> d.items.pop() | |
| 1349 (5, 6) | |
| 1350 >>> d.items | |
| 1351 [(0, 0), (1, 2), (3, 4), (7, 8)] | |
| 1352 >>> d.items.remove((1, 2)) | |
| 1353 >>> d.items | |
| 1354 [(0, 0), (3, 4), (7, 8)] | |
| 1355 >>> d.items.extend([(1, 2), (5, 6)]) | |
| 1356 >>> d.items | |
| 1357 [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)] | |
| 1358 """ | |
| 1359 | |
| 1360 def __init__(self, init_val=(), strict=True): | |
| 1361 OrderedDict.__init__(self, init_val, strict=strict) | |
| 1362 self._keys = self.keys | |
| 1363 self._values = self.values | |
| 1364 self._items = self.items | |
| 1365 self.keys = Keys(self) | |
| 1366 self.values = Values(self) | |
| 1367 self.items = Items(self) | |
| 1368 self._att_dict = { | |
| 1369 'keys': self.setkeys, | |
| 1370 'items': self.setitems, | |
| 1371 'values': self.setvalues, | |
| 1372 } | |
| 1373 | |
| 1374 def __setattr__(self, name, value): | |
| 1375 """Protect keys, items, and values.""" | |
| 1376 if not '_att_dict' in self.__dict__: | |
| 1377 object.__setattr__(self, name, value) | |
| 1378 else: | |
| 1379 try: | |
| 1380 fun = self._att_dict[name] | |
| 1381 except KeyError: | |
| 1382 OrderedDict.__setattr__(self, name, value) | |
| 1383 else: | |
| 1384 fun(value) | |
| 1385 | |
| 1386 if __name__ == '__main__': | |
| 1387 if INTP_VER < (2, 3): | |
| 1388 raise RuntimeError("Tests require Python v.2.3 or later") | |
| 1389 # turn off warnings for tests | |
| 1390 warnings.filterwarnings('ignore') | |
| 1391 # run the code tests in doctest format | |
| 1392 import doctest | |
| 1393 m = sys.modules.get('__main__') | |
| 1394 globs = m.__dict__.copy() | |
| 1395 globs.update({ | |
| 1396 'INTP_VER': INTP_VER, | |
| 1397 }) | |
| 1398 doctest.testmod(m, globs=globs) | |
| 1399 |
