github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/third_party/stdlib/collections.py (about) 1 '''This module implements specialized container datatypes providing 2 alternatives to Python's general purpose built-in containers, dict, 3 list, set, and tuple. 4 5 * namedtuple factory function for creating tuple subclasses with named fields 6 * deque list-like container with fast appends and pops on either end 7 * Counter dict subclass for counting hashable objects 8 * OrderedDict dict subclass that remembers the order entries were added 9 * defaultdict dict subclass that calls a factory function to supply missing values 10 11 ''' 12 13 __all__ = ['Counter', 'namedtuple', 'OrderedDict', 'deque'] # 'deque', 'defaultdict', 14 # For bootstrapping reasons, the collection ABCs are defined in _abcoll.py. 15 # They should however be considered an integral part of collections.py. 16 import _abcoll 17 # TODO: Support from foo import * syntax. 18 for name in _abcoll.__all__: 19 globals()[name] = getattr(_abcoll, name) 20 21 import _collections 22 deque = _collections.deque 23 #defaultdict = _collections.defaultdict 24 import operator 25 _itemgetter = operator.itemgetter 26 _eq = operator.eq 27 import keyword 28 _iskeyword = keyword.iskeyword 29 import sys as _sys 30 import heapq as _heapq 31 import itertools 32 _repeat = itertools.repeat 33 _chain = itertools.chain 34 _starmap = itertools.starmap 35 _imap = itertools.imap 36 37 #try: 38 import thread 39 _get_ident = thread.get_ident 40 #except ImportError: 41 # from dummy_thread import get_ident as _get_ident 42 43 44 ################################################################################ 45 ### OrderedDict 46 ################################################################################ 47 48 class OrderedDict(dict): 49 'Dictionary that remembers insertion order' 50 # An inherited dict maps keys to values. 51 # The inherited dict provides __getitem__, __len__, __contains__, and get. 52 # The remaining methods are order-aware. 53 # Big-O running times for all methods are the same as regular dictionaries. 54 55 # The internal self.__map dict maps keys to links in a doubly linked list. 56 # The circular doubly linked list starts and ends with a sentinel element. 57 # The sentinel element never gets deleted (this simplifies the algorithm). 58 # Each link is stored as a list of length three: [PREV, NEXT, KEY]. 59 60 def __init__(*args, **kwds): 61 '''Initialize an ordered dictionary. The signature is the same as 62 regular dictionaries, but keyword arguments are not recommended because 63 their insertion order is arbitrary. 64 65 ''' 66 if not args: 67 raise TypeError("descriptor '__init__' of 'OrderedDict' object " 68 "needs an argument") 69 self = args[0] 70 args = args[1:] 71 if len(args) > 1: 72 raise TypeError('expected at most 1 arguments, got %d' % len(args)) 73 try: 74 self.__root 75 except AttributeError: 76 self.__root = root = [] # sentinel node 77 root[:] = [root, root, None] 78 self.__map = {} 79 self.__update(*args, **kwds) 80 81 def __setitem__(self, key, value, dict_setitem=dict.__setitem__): 82 'od.__setitem__(i, y) <==> od[i]=y' 83 # Setting a new item creates a new link at the end of the linked list, 84 # and the inherited dictionary is updated with the new key/value pair. 85 if key not in self: 86 root = self.__root 87 last = root[0] 88 last[1] = root[0] = self.__map[key] = [last, root, key] 89 return dict_setitem(self, key, value) 90 91 def __delitem__(self, key, dict_delitem=dict.__delitem__): 92 'od.__delitem__(y) <==> del od[y]' 93 # Deleting an existing item uses self.__map to find the link which gets 94 # removed by updating the links in the predecessor and successor nodes. 95 dict_delitem(self, key) 96 link_prev, link_next, _ = self.__map.pop(key) 97 link_prev[1] = link_next # update link_prev[NEXT] 98 link_next[0] = link_prev # update link_next[PREV] 99 100 def __iter__(self): 101 'od.__iter__() <==> iter(od)' 102 # Traverse the linked list in order. 103 root = self.__root 104 curr = root[1] # start at the first node 105 while curr is not root: 106 yield curr[2] # yield the curr[KEY] 107 curr = curr[1] # move to next node 108 109 def __reversed__(self): 110 'od.__reversed__() <==> reversed(od)' 111 # Traverse the linked list in reverse order. 112 root = self.__root 113 curr = root[0] # start at the last node 114 while curr is not root: 115 yield curr[2] # yield the curr[KEY] 116 curr = curr[0] # move to previous node 117 118 def clear(self): 119 'od.clear() -> None. Remove all items from od.' 120 root = self.__root 121 root[:] = [root, root, None] 122 self.__map.clear() 123 dict.clear(self) 124 125 # -- the following methods do not depend on the internal structure -- 126 127 def keys(self): 128 'od.keys() -> list of keys in od' 129 return list(self) 130 131 def values(self): 132 'od.values() -> list of values in od' 133 return [self[key] for key in self] 134 135 def items(self): 136 'od.items() -> list of (key, value) pairs in od' 137 return [(key, self[key]) for key in self] 138 139 def iterkeys(self): 140 'od.iterkeys() -> an iterator over the keys in od' 141 return iter(self) 142 143 def itervalues(self): 144 'od.itervalues -> an iterator over the values in od' 145 for k in self: 146 yield self[k] 147 148 def iteritems(self): 149 'od.iteritems -> an iterator over the (key, value) pairs in od' 150 for k in self: 151 yield (k, self[k]) 152 153 update = MutableMapping.update 154 155 __update = update # let subclasses override update without breaking __init__ 156 157 __marker = object() 158 159 def pop(self, key, default=__marker): 160 '''od.pop(k[,d]) -> v, remove specified key and return the corresponding 161 value. If key is not found, d is returned if given, otherwise KeyError 162 is raised. 163 164 ''' 165 if key in self: 166 result = self[key] 167 del self[key] 168 return result 169 if default is self.__marker: 170 raise KeyError(key) 171 return default 172 173 def setdefault(self, key, default=None): 174 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' 175 if key in self: 176 return self[key] 177 self[key] = default 178 return default 179 180 def popitem(self, last=True): 181 '''od.popitem() -> (k, v), return and remove a (key, value) pair. 182 Pairs are returned in LIFO order if last is true or FIFO order if false. 183 184 ''' 185 if not self: 186 raise KeyError('dictionary is empty') 187 key = next(reversed(self) if last else iter(self)) 188 value = self.pop(key) 189 return key, value 190 191 def __repr__(self, _repr_running={}): 192 'od.__repr__() <==> repr(od)' 193 call_key = id(self), _get_ident() 194 if call_key in _repr_running: 195 return '...' 196 _repr_running[call_key] = 1 197 try: 198 if not self: 199 return '%s()' % (self.__class__.__name__,) 200 return '%s(%r)' % (self.__class__.__name__, self.items()) 201 finally: 202 del _repr_running[call_key] 203 204 def __reduce__(self): 205 'Return state information for pickling' 206 items = [[k, self[k]] for k in self] 207 inst_dict = vars(self).copy() 208 for k in vars(OrderedDict()): 209 inst_dict.pop(k, None) 210 if inst_dict: 211 return (self.__class__, (items,), inst_dict) 212 return self.__class__, (items,) 213 214 def copy(self): 215 'od.copy() -> a shallow copy of od' 216 return self.__class__(self) 217 218 @classmethod 219 def fromkeys(cls, iterable, value=None): 220 '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S. 221 If not specified, the value defaults to None. 222 223 ''' 224 self = cls() 225 for key in iterable: 226 self[key] = value 227 return self 228 229 def __eq__(self, other): 230 '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive 231 while comparison to a regular mapping is order-insensitive. 232 233 ''' 234 if isinstance(other, OrderedDict): 235 return dict.__eq__(self, other) and all(_imap(_eq, self, other)) 236 return dict.__eq__(self, other) 237 238 def __ne__(self, other): 239 'od.__ne__(y) <==> od!=y' 240 return not self == other 241 242 # -- the following methods support python 3.x style dictionary views -- 243 244 def viewkeys(self): 245 "od.viewkeys() -> a set-like object providing a view on od's keys" 246 return KeysView(self) 247 248 def viewvalues(self): 249 "od.viewvalues() -> an object providing a view on od's values" 250 return ValuesView(self) 251 252 def viewitems(self): 253 "od.viewitems() -> a set-like object providing a view on od's items" 254 return ItemsView(self) 255 256 257 ################################################################################ 258 ### namedtuple 259 ################################################################################ 260 261 _class_template = '''\ 262 class {typename}(tuple): 263 '{typename}({arg_list})' 264 265 __slots__ = () 266 267 _fields = {field_names!r} 268 269 def __new__(_cls, {arg_list}): 270 'Create new instance of {typename}({arg_list})' 271 return _tuple.__new__(_cls, ({arg_list})) 272 273 @classmethod 274 def _make(cls, iterable, new=tuple.__new__, len=len): 275 'Make a new {typename} object from a sequence or iterable' 276 result = new(cls, iterable) 277 if len(result) != {num_fields:d}: 278 raise TypeError('Expected {num_fields:d} arguments, got %d' % len(result)) 279 return result 280 281 def __repr__(self): 282 'Return a nicely formatted representation string' 283 return '{typename}({repr_fmt})' % self 284 285 def _asdict(self): 286 'Return a new OrderedDict which maps field names to their values' 287 return OrderedDict(zip(self._fields, self)) 288 289 def _replace(_self, **kwds): 290 'Return a new {typename} object replacing specified fields with new values' 291 result = _self._make(map(kwds.pop, {field_names!r}, _self)) 292 if kwds: 293 raise ValueError('Got unexpected field names: %r' % kwds.keys()) 294 return result 295 296 def __getnewargs__(self): 297 'Return self as a plain tuple. Used by copy and pickle.' 298 return tuple(self) 299 300 __dict__ = _property(_asdict) 301 302 def __getstate__(self): 303 'Exclude the OrderedDict from pickling' 304 pass 305 306 {field_defs} 307 ''' 308 309 _repr_template = '{name}=%r' 310 311 _field_template = '''\ 312 {name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}') 313 ''' 314 315 #def namedtuple(typename, field_names, verbose=False, rename=False): 316 # """Returns a new subclass of tuple with named fields. 317 # 318 # >>> Point = namedtuple('Point', ['x', 'y']) 319 # >>> Point.__doc__ # docstring for the new class 320 # 'Point(x, y)' 321 # >>> p = Point(11, y=22) # instantiate with positional args or keywords 322 # >>> p[0] + p[1] # indexable like a plain tuple 323 # 33 324 # >>> x, y = p # unpack like a regular tuple 325 # >>> x, y 326 # (11, 22) 327 # >>> p.x + p.y # fields also accessible by name 328 # 33 329 # >>> d = p._asdict() # convert to a dictionary 330 # >>> d['x'] 331 # 11 332 # >>> Point(**d) # convert from a dictionary 333 # Point(x=11, y=22) 334 # >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields 335 # Point(x=100, y=22) 336 # 337 # """ 338 # 339 # # Validate the field names. At the user's option, either generate an error 340 # # message or automatically replace the field name with a valid name. 341 # if isinstance(field_names, basestring): 342 # field_names = field_names.replace(',', ' ').split() 343 # field_names = map(str, field_names) 344 # typename = str(typename) 345 # if rename: 346 # seen = set() 347 # for index, name in enumerate(field_names): 348 # if (not all(c.isalnum() or c=='_' for c in name) 349 # or _iskeyword(name) 350 # or not name 351 # or name[0].isdigit() 352 # or name.startswith('_') 353 # or name in seen): 354 # field_names[index] = '_%d' % index 355 # seen.add(name) 356 # for name in [typename] + field_names: 357 # if type(name) != str: 358 # raise TypeError('Type names and field names must be strings') 359 # if not all(c.isalnum() or c=='_' for c in name): 360 # raise ValueError('Type names and field names can only contain ' 361 # 'alphanumeric characters and underscores: %r' % name) 362 # if _iskeyword(name): 363 # raise ValueError('Type names and field names cannot be a ' 364 # 'keyword: %r' % name) 365 # if name[0].isdigit(): 366 # raise ValueError('Type names and field names cannot start with ' 367 # 'a number: %r' % name) 368 # seen = set() 369 # for name in field_names: 370 # if name.startswith('_') and not rename: 371 # raise ValueError('Field names cannot start with an underscore: ' 372 # '%r' % name) 373 # if name in seen: 374 # raise ValueError('Encountered duplicate field name: %r' % name) 375 # seen.add(name) 376 # 377 # # Fill-in the class template 378 # class_definition = _class_template.format( 379 # typename = typename, 380 # field_names = tuple(field_names), 381 # num_fields = len(field_names), 382 # arg_list = repr(tuple(field_names)).replace("'", "")[1:-1], 383 # repr_fmt = ', '.join(_repr_template.format(name=name) 384 # for name in field_names), 385 # field_defs = '\n'.join(_field_template.format(index=index, name=name) 386 # for index, name in enumerate(field_names)) 387 # ) 388 # if verbose: 389 # print class_definition 390 # 391 # # Execute the template string in a temporary namespace and support 392 # # tracing utilities by setting a value for frame.f_globals['__name__'] 393 # namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, 394 # OrderedDict=OrderedDict, _property=property, _tuple=tuple) 395 # try: 396 # exec class_definition in namespace 397 # except SyntaxError as e: 398 # raise SyntaxError(e.message + ':\n' + class_definition) 399 # result = namespace[typename] 400 # 401 # # For pickling to work, the __module__ variable needs to be set to the frame 402 # # where the named tuple is created. Bypass this step in environments where 403 # # sys._getframe is not defined (Jython for example) or sys._getframe is not 404 # # defined for arguments greater than 0 (IronPython). 405 # try: 406 # result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') 407 # except (AttributeError, ValueError): 408 # pass 409 # 410 # return result 411 412 413 ######################################################################## 414 ### Counter 415 ######################################################################## 416 417 class Counter(dict): 418 '''Dict subclass for counting hashable items. Sometimes called a bag 419 or multiset. Elements are stored as dictionary keys and their counts 420 are stored as dictionary values. 421 422 >>> c = Counter('abcdeabcdabcaba') # count elements from a string 423 424 >>> c.most_common(3) # three most common elements 425 [('a', 5), ('b', 4), ('c', 3)] 426 >>> sorted(c) # list all unique elements 427 ['a', 'b', 'c', 'd', 'e'] 428 >>> ''.join(sorted(c.elements())) # list elements with repetitions 429 'aaaaabbbbcccdde' 430 >>> sum(c.values()) # total of all counts 431 15 432 433 >>> c['a'] # count of letter 'a' 434 5 435 >>> for elem in 'shazam': # update counts from an iterable 436 ... c[elem] += 1 # by adding 1 to each element's count 437 >>> c['a'] # now there are seven 'a' 438 7 439 >>> del c['b'] # remove all 'b' 440 >>> c['b'] # now there are zero 'b' 441 0 442 443 >>> d = Counter('simsalabim') # make another counter 444 >>> c.update(d) # add in the second counter 445 >>> c['a'] # now there are nine 'a' 446 9 447 448 >>> c.clear() # empty the counter 449 >>> c 450 Counter() 451 452 Note: If a count is set to zero or reduced to zero, it will remain 453 in the counter until the entry is deleted or the counter is cleared: 454 455 >>> c = Counter('aaabbc') 456 >>> c['b'] -= 2 # reduce the count of 'b' by two 457 >>> c.most_common() # 'b' is still in, but its count is zero 458 [('a', 3), ('c', 1), ('b', 0)] 459 460 ''' 461 # References: 462 # http://en.wikipedia.org/wiki/Multiset 463 # http://www.gnu.org/software/smalltalk/manual-base/html_node/Bag.html 464 # http://www.demo2s.com/Tutorial/Cpp/0380__set-multiset/Catalog0380__set-multiset.htm 465 # http://code.activestate.com/recipes/259174/ 466 # Knuth, TAOCP Vol. II section 4.6.3 467 468 def __init__(*args, **kwds): 469 '''Create a new, empty Counter object. And if given, count elements 470 from an input iterable. Or, initialize the count from another mapping 471 of elements to their counts. 472 473 >>> c = Counter() # a new, empty counter 474 >>> c = Counter('gallahad') # a new counter from an iterable 475 >>> c = Counter({'a': 4, 'b': 2}) # a new counter from a mapping 476 >>> c = Counter(a=4, b=2) # a new counter from keyword args 477 478 ''' 479 if not args: 480 raise TypeError("descriptor '__init__' of 'Counter' object " 481 "needs an argument") 482 self = args[0] 483 args = args[1:] 484 if len(args) > 1: 485 raise TypeError('expected at most 1 arguments, got %d' % len(args)) 486 super(Counter, self).__init__() 487 self.update(*args, **kwds) 488 489 def __missing__(self, key): 490 'The count of elements not in the Counter is zero.' 491 # Needed so that self[missing_item] does not raise KeyError 492 return 0 493 494 def most_common(self, n=None): 495 '''List the n most common elements and their counts from the most 496 common to the least. If n is None, then list all element counts. 497 498 >>> Counter('abcdeabcdabcaba').most_common(3) 499 [('a', 5), ('b', 4), ('c', 3)] 500 501 ''' 502 # Emulate Bag.sortedByCount from Smalltalk 503 if n is None: 504 return sorted(self.iteritems(), key=_itemgetter(1), reverse=True) 505 return _heapq.nlargest(n, self.iteritems(), key=_itemgetter(1)) 506 507 def elements(self): 508 '''Iterator over elements repeating each as many times as its count. 509 510 >>> c = Counter('ABCABC') 511 >>> sorted(c.elements()) 512 ['A', 'A', 'B', 'B', 'C', 'C'] 513 514 # Knuth's example for prime factors of 1836: 2**2 * 3**3 * 17**1 515 >>> prime_factors = Counter({2: 2, 3: 3, 17: 1}) 516 >>> product = 1 517 >>> for factor in prime_factors.elements(): # loop over factors 518 ... product *= factor # and multiply them 519 >>> product 520 1836 521 522 Note, if an element's count has been set to zero or is a negative 523 number, elements() will ignore it. 524 525 ''' 526 # Emulate Bag.do from Smalltalk and Multiset.begin from C++. 527 return _chain.from_iterable(_starmap(_repeat, self.iteritems())) 528 529 # Override dict methods where necessary 530 531 @classmethod 532 def fromkeys(cls, iterable, v=None): 533 # There is no equivalent method for counters because setting v=1 534 # means that no element can have a count greater than one. 535 raise NotImplementedError( 536 'Counter.fromkeys() is undefined. Use Counter(iterable) instead.') 537 538 def update(*args, **kwds): 539 '''Like dict.update() but add counts instead of replacing them. 540 541 Source can be an iterable, a dictionary, or another Counter instance. 542 543 >>> c = Counter('which') 544 >>> c.update('witch') # add elements from another iterable 545 >>> d = Counter('watch') 546 >>> c.update(d) # add elements from another counter 547 >>> c['h'] # four 'h' in which, witch, and watch 548 4 549 550 ''' 551 # The regular dict.update() operation makes no sense here because the 552 # replace behavior results in the some of original untouched counts 553 # being mixed-in with all of the other counts for a mismash that 554 # doesn't have a straight-forward interpretation in most counting 555 # contexts. Instead, we implement straight-addition. Both the inputs 556 # and outputs are allowed to contain zero and negative counts. 557 558 if not args: 559 raise TypeError("descriptor 'update' of 'Counter' object " 560 "needs an argument") 561 self = args[0] 562 args = args[1:] 563 if len(args) > 1: 564 raise TypeError('expected at most 1 arguments, got %d' % len(args)) 565 iterable = args[0] if args else None 566 if iterable is not None: 567 if isinstance(iterable, Mapping): 568 if self: 569 self_get = self.get 570 for elem, count in iterable.iteritems(): 571 self[elem] = self_get(elem, 0) + count 572 else: 573 super(Counter, self).update(iterable) # fast path when counter is empty 574 else: 575 self_get = self.get 576 for elem in iterable: 577 self[elem] = self_get(elem, 0) + 1 578 if kwds: 579 self.update(kwds) 580 581 def subtract(*args, **kwds): 582 '''Like dict.update() but subtracts counts instead of replacing them. 583 Counts can be reduced below zero. Both the inputs and outputs are 584 allowed to contain zero and negative counts. 585 586 Source can be an iterable, a dictionary, or another Counter instance. 587 588 >>> c = Counter('which') 589 >>> c.subtract('witch') # subtract elements from another iterable 590 >>> c.subtract(Counter('watch')) # subtract elements from another counter 591 >>> c['h'] # 2 in which, minus 1 in witch, minus 1 in watch 592 0 593 >>> c['w'] # 1 in which, minus 1 in witch, minus 1 in watch 594 -1 595 596 ''' 597 if not args: 598 raise TypeError("descriptor 'subtract' of 'Counter' object " 599 "needs an argument") 600 self = args[0] 601 args = args[1:] 602 if len(args) > 1: 603 raise TypeError('expected at most 1 arguments, got %d' % len(args)) 604 iterable = args[0] if args else None 605 if iterable is not None: 606 self_get = self.get 607 if isinstance(iterable, Mapping): 608 for elem, count in iterable.items(): 609 self[elem] = self_get(elem, 0) - count 610 else: 611 for elem in iterable: 612 self[elem] = self_get(elem, 0) - 1 613 if kwds: 614 self.subtract(kwds) 615 616 def copy(self): 617 'Return a shallow copy.' 618 return self.__class__(self) 619 620 def __reduce__(self): 621 return self.__class__, (dict(self),) 622 623 def __delitem__(self, elem): 624 'Like dict.__delitem__() but does not raise KeyError for missing values.' 625 if elem in self: 626 super(Counter, self).__delitem__(elem) 627 628 def __repr__(self): 629 if not self: 630 return '%s()' % self.__class__.__name__ 631 items = ', '.join(map('%r: %r'.__mod__, self.most_common())) 632 return '%s({%s})' % (self.__class__.__name__, items) 633 634 # Multiset-style mathematical operations discussed in: 635 # Knuth TAOCP Volume II section 4.6.3 exercise 19 636 # and at http://en.wikipedia.org/wiki/Multiset 637 # 638 # Outputs guaranteed to only include positive counts. 639 # 640 # To strip negative and zero counts, add-in an empty counter: 641 # c += Counter() 642 643 def __add__(self, other): 644 '''Add counts from two counters. 645 646 >>> Counter('abbb') + Counter('bcc') 647 Counter({'b': 4, 'c': 2, 'a': 1}) 648 649 ''' 650 if not isinstance(other, Counter): 651 return NotImplemented 652 result = Counter() 653 for elem, count in self.items(): 654 newcount = count + other[elem] 655 if newcount > 0: 656 result[elem] = newcount 657 for elem, count in other.items(): 658 if elem not in self and count > 0: 659 result[elem] = count 660 return result 661 662 def __sub__(self, other): 663 ''' Subtract count, but keep only results with positive counts. 664 665 >>> Counter('abbbc') - Counter('bccd') 666 Counter({'b': 2, 'a': 1}) 667 668 ''' 669 if not isinstance(other, Counter): 670 return NotImplemented 671 result = Counter() 672 for elem, count in self.items(): 673 newcount = count - other[elem] 674 if newcount > 0: 675 result[elem] = newcount 676 for elem, count in other.items(): 677 if elem not in self and count < 0: 678 result[elem] = 0 - count 679 return result 680 681 def __or__(self, other): 682 '''Union is the maximum of value in either of the input counters. 683 684 >>> Counter('abbb') | Counter('bcc') 685 Counter({'b': 3, 'c': 2, 'a': 1}) 686 687 ''' 688 if not isinstance(other, Counter): 689 return NotImplemented 690 result = Counter() 691 for elem, count in self.items(): 692 other_count = other[elem] 693 newcount = other_count if count < other_count else count 694 if newcount > 0: 695 result[elem] = newcount 696 for elem, count in other.items(): 697 if elem not in self and count > 0: 698 result[elem] = count 699 return result 700 701 def __and__(self, other): 702 ''' Intersection is the minimum of corresponding counts. 703 704 >>> Counter('abbb') & Counter('bcc') 705 Counter({'b': 1}) 706 707 ''' 708 if not isinstance(other, Counter): 709 return NotImplemented 710 result = Counter() 711 for elem, count in self.items(): 712 other_count = other[elem] 713 newcount = count if count < other_count else other_count 714 if newcount > 0: 715 result[elem] = newcount 716 return result 717 718 719 if __name__ == '__main__': 720 pass 721 # # verify that instances can be pickled 722 # from cPickle import loads, dumps 723 # Point = namedtuple('Point', 'x, y', True) 724 # p = Point(x=10, y=20) 725 # assert p == loads(dumps(p)) 726 # 727 # # test and demonstrate ability to override methods 728 # class Point(namedtuple('Point', 'x y')): 729 # __slots__ = () 730 # @property 731 # def hypot(self): 732 # return (self.x ** 2 + self.y ** 2) ** 0.5 733 # def __str__(self): 734 # return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) 735 # 736 # for p in Point(3, 4), Point(14, 5/7.): 737 # print p 738 # 739 # class Point(namedtuple('Point', 'x y')): 740 # 'Point class with optimized _make() and _replace() without error-checking' 741 # __slots__ = () 742 # _make = classmethod(tuple.__new__) 743 # def _replace(self, _map=map, **kwds): 744 # return self._make(_map(kwds.get, ('x', 'y'), self)) 745 # 746 # print Point(11, 22)._replace(x=100) 747 # 748 # Point3D = namedtuple('Point3D', Point._fields + ('z',)) 749 # print Point3D.__doc__ 750 # 751 # import doctest 752 # TestResults = namedtuple('TestResults', 'failed attempted') 753 # print TestResults(*doctest.testmod())