PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/External.LCA_RESTRICTED/Languages/CPython/27/Lib/collections.py

http://github.com/IronLanguages/main
Python | 667 lines | 581 code | 38 blank | 48 comment | 35 complexity | df7727547f7507272c251e04c6ae48d4 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. __all__ = ['Counter', 'deque', 'defaultdict', 'namedtuple', 'OrderedDict']
  2. # For bootstrapping reasons, the collection ABCs are defined in _abcoll.py.
  3. # They should however be considered an integral part of collections.py.
  4. from _abcoll import *
  5. import _abcoll
  6. __all__ += _abcoll.__all__
  7. from _collections import deque, defaultdict
  8. from operator import itemgetter as _itemgetter
  9. from keyword import iskeyword as _iskeyword
  10. import sys as _sys
  11. import heapq as _heapq
  12. from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
  13. try:
  14. from thread import get_ident as _get_ident
  15. except ImportError:
  16. from dummy_thread import get_ident as _get_ident
  17. ################################################################################
  18. ### OrderedDict
  19. ################################################################################
  20. class OrderedDict(dict):
  21. 'Dictionary that remembers insertion order'
  22. # An inherited dict maps keys to values.
  23. # The inherited dict provides __getitem__, __len__, __contains__, and get.
  24. # The remaining methods are order-aware.
  25. # Big-O running times for all methods are the same as regular dictionaries.
  26. # The internal self.__map dict maps keys to links in a doubly linked list.
  27. # The circular doubly linked list starts and ends with a sentinel element.
  28. # The sentinel element never gets deleted (this simplifies the algorithm).
  29. # Each link is stored as a list of length three: [PREV, NEXT, KEY].
  30. def __init__(self, *args, **kwds):
  31. '''Initialize an ordered dictionary. The signature is the same as
  32. regular dictionaries, but keyword arguments are not recommended because
  33. their insertion order is arbitrary.
  34. '''
  35. if len(args) > 1:
  36. raise TypeError('expected at most 1 arguments, got %d' % len(args))
  37. try:
  38. self.__root
  39. except AttributeError:
  40. self.__root = root = [] # sentinel node
  41. root[:] = [root, root, None]
  42. self.__map = {}
  43. self.__update(*args, **kwds)
  44. def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__):
  45. 'od.__setitem__(i, y) <==> od[i]=y'
  46. # Setting a new item creates a new link at the end of the linked list,
  47. # and the inherited dictionary is updated with the new key/value pair.
  48. if key not in self:
  49. root = self.__root
  50. last = root[PREV]
  51. last[NEXT] = root[PREV] = self.__map[key] = [last, root, key]
  52. dict_setitem(self, key, value)
  53. def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__):
  54. 'od.__delitem__(y) <==> del od[y]'
  55. # Deleting an existing item uses self.__map to find the link which gets
  56. # removed by updating the links in the predecessor and successor nodes.
  57. dict_delitem(self, key)
  58. link_prev, link_next, key = self.__map.pop(key)
  59. link_prev[NEXT] = link_next
  60. link_next[PREV] = link_prev
  61. def __iter__(self):
  62. 'od.__iter__() <==> iter(od)'
  63. # Traverse the linked list in order.
  64. NEXT, KEY = 1, 2
  65. root = self.__root
  66. curr = root[NEXT]
  67. while curr is not root:
  68. yield curr[KEY]
  69. curr = curr[NEXT]
  70. def __reversed__(self):
  71. 'od.__reversed__() <==> reversed(od)'
  72. # Traverse the linked list in reverse order.
  73. PREV, KEY = 0, 2
  74. root = self.__root
  75. curr = root[PREV]
  76. while curr is not root:
  77. yield curr[KEY]
  78. curr = curr[PREV]
  79. def clear(self):
  80. 'od.clear() -> None. Remove all items from od.'
  81. for node in self.__map.itervalues():
  82. del node[:]
  83. root = self.__root
  84. root[:] = [root, root, None]
  85. self.__map.clear()
  86. dict.clear(self)
  87. # -- the following methods do not depend on the internal structure --
  88. def keys(self):
  89. 'od.keys() -> list of keys in od'
  90. return list(self)
  91. def values(self):
  92. 'od.values() -> list of values in od'
  93. return [self[key] for key in self]
  94. def items(self):
  95. 'od.items() -> list of (key, value) pairs in od'
  96. return [(key, self[key]) for key in self]
  97. def iterkeys(self):
  98. 'od.iterkeys() -> an iterator over the keys in od'
  99. return iter(self)
  100. def itervalues(self):
  101. 'od.itervalues -> an iterator over the values in od'
  102. for k in self:
  103. yield self[k]
  104. def iteritems(self):
  105. 'od.iteritems -> an iterator over the (key, value) pairs in od'
  106. for k in self:
  107. yield (k, self[k])
  108. update = MutableMapping.update
  109. __update = update # let subclasses override update without breaking __init__
  110. __marker = object()
  111. def pop(self, key, default=__marker):
  112. '''od.pop(k[,d]) -> v, remove specified key and return the corresponding
  113. value. If key is not found, d is returned if given, otherwise KeyError
  114. is raised.
  115. '''
  116. if key in self:
  117. result = self[key]
  118. del self[key]
  119. return result
  120. if default is self.__marker:
  121. raise KeyError(key)
  122. return default
  123. def setdefault(self, key, default=None):
  124. 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
  125. if key in self:
  126. return self[key]
  127. self[key] = default
  128. return default
  129. def popitem(self, last=True):
  130. '''od.popitem() -> (k, v), return and remove a (key, value) pair.
  131. Pairs are returned in LIFO order if last is true or FIFO order if false.
  132. '''
  133. if not self:
  134. raise KeyError('dictionary is empty')
  135. key = next(reversed(self) if last else iter(self))
  136. value = self.pop(key)
  137. return key, value
  138. def __repr__(self, _repr_running={}):
  139. 'od.__repr__() <==> repr(od)'
  140. call_key = id(self), _get_ident()
  141. if call_key in _repr_running:
  142. return '...'
  143. _repr_running[call_key] = 1
  144. try:
  145. if not self:
  146. return '%s()' % (self.__class__.__name__,)
  147. return '%s(%r)' % (self.__class__.__name__, self.items())
  148. finally:
  149. del _repr_running[call_key]
  150. def __reduce__(self):
  151. 'Return state information for pickling'
  152. items = [[k, self[k]] for k in self]
  153. inst_dict = vars(self).copy()
  154. for k in vars(OrderedDict()):
  155. inst_dict.pop(k, None)
  156. if inst_dict:
  157. return (self.__class__, (items,), inst_dict)
  158. return self.__class__, (items,)
  159. def copy(self):
  160. 'od.copy() -> a shallow copy of od'
  161. return self.__class__(self)
  162. @classmethod
  163. def fromkeys(cls, iterable, value=None):
  164. '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S.
  165. If not specified, the value defaults to None.
  166. '''
  167. self = cls()
  168. for key in iterable:
  169. self[key] = value
  170. return self
  171. def __eq__(self, other):
  172. '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
  173. while comparison to a regular mapping is order-insensitive.
  174. '''
  175. if isinstance(other, OrderedDict):
  176. return len(self)==len(other) and self.items() == other.items()
  177. return dict.__eq__(self, other)
  178. def __ne__(self, other):
  179. 'od.__ne__(y) <==> od!=y'
  180. return not self == other
  181. # -- the following methods support python 3.x style dictionary views --
  182. def viewkeys(self):
  183. "od.viewkeys() -> a set-like object providing a view on od's keys"
  184. return KeysView(self)
  185. def viewvalues(self):
  186. "od.viewvalues() -> an object providing a view on od's values"
  187. return ValuesView(self)
  188. def viewitems(self):
  189. "od.viewitems() -> a set-like object providing a view on od's items"
  190. return ItemsView(self)
  191. ################################################################################
  192. ### namedtuple
  193. ################################################################################
  194. def namedtuple(typename, field_names, verbose=False, rename=False):
  195. """Returns a new subclass of tuple with named fields.
  196. >>> Point = namedtuple('Point', 'x y')
  197. >>> Point.__doc__ # docstring for the new class
  198. 'Point(x, y)'
  199. >>> p = Point(11, y=22) # instantiate with positional args or keywords
  200. >>> p[0] + p[1] # indexable like a plain tuple
  201. 33
  202. >>> x, y = p # unpack like a regular tuple
  203. >>> x, y
  204. (11, 22)
  205. >>> p.x + p.y # fields also accessable by name
  206. 33
  207. >>> d = p._asdict() # convert to a dictionary
  208. >>> d['x']
  209. 11
  210. >>> Point(**d) # convert from a dictionary
  211. Point(x=11, y=22)
  212. >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields
  213. Point(x=100, y=22)
  214. """
  215. # Parse and validate the field names. Validation serves two purposes,
  216. # generating informative error messages and preventing template injection attacks.
  217. if isinstance(field_names, basestring):
  218. field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas
  219. field_names = tuple(map(str, field_names))
  220. if rename:
  221. names = list(field_names)
  222. seen = set()
  223. for i, name in enumerate(names):
  224. if (not all(c.isalnum() or c=='_' for c in name) or _iskeyword(name)
  225. or not name or name[0].isdigit() or name.startswith('_')
  226. or name in seen):
  227. names[i] = '_%d' % i
  228. seen.add(name)
  229. field_names = tuple(names)
  230. for name in (typename,) + field_names:
  231. if not all(c.isalnum() or c=='_' for c in name):
  232. raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name)
  233. if _iskeyword(name):
  234. raise ValueError('Type names and field names cannot be a keyword: %r' % name)
  235. if name[0].isdigit():
  236. raise ValueError('Type names and field names cannot start with a number: %r' % name)
  237. seen_names = set()
  238. for name in field_names:
  239. if name.startswith('_') and not rename:
  240. raise ValueError('Field names cannot start with an underscore: %r' % name)
  241. if name in seen_names:
  242. raise ValueError('Encountered duplicate field name: %r' % name)
  243. seen_names.add(name)
  244. # Create and fill-in the class template
  245. numfields = len(field_names)
  246. argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
  247. reprtxt = ', '.join('%s=%%r' % name for name in field_names)
  248. template = '''class %(typename)s(tuple):
  249. '%(typename)s(%(argtxt)s)' \n
  250. __slots__ = () \n
  251. _fields = %(field_names)r \n
  252. def __new__(_cls, %(argtxt)s):
  253. 'Create new instance of %(typename)s(%(argtxt)s)'
  254. return _tuple.__new__(_cls, (%(argtxt)s)) \n
  255. @classmethod
  256. def _make(cls, iterable, new=tuple.__new__, len=len):
  257. 'Make a new %(typename)s object from a sequence or iterable'
  258. result = new(cls, iterable)
  259. if len(result) != %(numfields)d:
  260. raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))
  261. return result \n
  262. def __repr__(self):
  263. 'Return a nicely formatted representation string'
  264. return '%(typename)s(%(reprtxt)s)' %% self \n
  265. def _asdict(self):
  266. 'Return a new OrderedDict which maps field names to their values'
  267. return OrderedDict(zip(self._fields, self)) \n
  268. def _replace(_self, **kwds):
  269. 'Return a new %(typename)s object replacing specified fields with new values'
  270. result = _self._make(map(kwds.pop, %(field_names)r, _self))
  271. if kwds:
  272. raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
  273. return result \n
  274. def __getnewargs__(self):
  275. 'Return self as a plain tuple. Used by copy and pickle.'
  276. return tuple(self) \n\n''' % locals()
  277. for i, name in enumerate(field_names):
  278. template += " %s = _property(_itemgetter(%d), doc='Alias for field number %d')\n" % (name, i, i)
  279. if verbose:
  280. print template
  281. # Execute the template string in a temporary namespace and
  282. # support tracing utilities by setting a value for frame.f_globals['__name__']
  283. namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
  284. OrderedDict=OrderedDict, _property=property, _tuple=tuple)
  285. try:
  286. exec template in namespace
  287. except SyntaxError, e:
  288. raise SyntaxError(e.message + ':\n' + template)
  289. result = namespace[typename]
  290. # For pickling to work, the __module__ variable needs to be set to the frame
  291. # where the named tuple is created. Bypass this step in enviroments where
  292. # sys._getframe is not defined (Jython for example) or sys._getframe is not
  293. # defined for arguments greater than 0 (IronPython).
  294. try:
  295. result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
  296. except (AttributeError, ValueError):
  297. pass
  298. return result
  299. ########################################################################
  300. ### Counter
  301. ########################################################################
  302. class Counter(dict):
  303. '''Dict subclass for counting hashable items. Sometimes called a bag
  304. or multiset. Elements are stored as dictionary keys and their counts
  305. are stored as dictionary values.
  306. >>> c = Counter('abcdeabcdabcaba') # count elements from a string
  307. >>> c.most_common(3) # three most common elements
  308. [('a', 5), ('b', 4), ('c', 3)]
  309. >>> sorted(c) # list all unique elements
  310. ['a', 'b', 'c', 'd', 'e']
  311. >>> ''.join(sorted(c.elements())) # list elements with repetitions
  312. 'aaaaabbbbcccdde'
  313. >>> sum(c.values()) # total of all counts
  314. 15
  315. >>> c['a'] # count of letter 'a'
  316. 5
  317. >>> for elem in 'shazam': # update counts from an iterable
  318. ... c[elem] += 1 # by adding 1 to each element's count
  319. >>> c['a'] # now there are seven 'a'
  320. 7
  321. >>> del c['b'] # remove all 'b'
  322. >>> c['b'] # now there are zero 'b'
  323. 0
  324. >>> d = Counter('simsalabim') # make another counter
  325. >>> c.update(d) # add in the second counter
  326. >>> c['a'] # now there are nine 'a'
  327. 9
  328. >>> c.clear() # empty the counter
  329. >>> c
  330. Counter()
  331. Note: If a count is set to zero or reduced to zero, it will remain
  332. in the counter until the entry is deleted or the counter is cleared:
  333. >>> c = Counter('aaabbc')
  334. >>> c['b'] -= 2 # reduce the count of 'b' by two
  335. >>> c.most_common() # 'b' is still in, but its count is zero
  336. [('a', 3), ('c', 1), ('b', 0)]
  337. '''
  338. # References:
  339. # http://en.wikipedia.org/wiki/Multiset
  340. # http://www.gnu.org/software/smalltalk/manual-base/html_node/Bag.html
  341. # http://www.demo2s.com/Tutorial/Cpp/0380__set-multiset/Catalog0380__set-multiset.htm
  342. # http://code.activestate.com/recipes/259174/
  343. # Knuth, TAOCP Vol. II section 4.6.3
  344. def __init__(self, iterable=None, **kwds):
  345. '''Create a new, empty Counter object. And if given, count elements
  346. from an input iterable. Or, initialize the count from another mapping
  347. of elements to their counts.
  348. >>> c = Counter() # a new, empty counter
  349. >>> c = Counter('gallahad') # a new counter from an iterable
  350. >>> c = Counter({'a': 4, 'b': 2}) # a new counter from a mapping
  351. >>> c = Counter(a=4, b=2) # a new counter from keyword args
  352. '''
  353. super(Counter, self).__init__()
  354. self.update(iterable, **kwds)
  355. def __missing__(self, key):
  356. 'The count of elements not in the Counter is zero.'
  357. # Needed so that self[missing_item] does not raise KeyError
  358. return 0
  359. def most_common(self, n=None):
  360. '''List the n most common elements and their counts from the most
  361. common to the least. If n is None, then list all element counts.
  362. >>> Counter('abcdeabcdabcaba').most_common(3)
  363. [('a', 5), ('b', 4), ('c', 3)]
  364. '''
  365. # Emulate Bag.sortedByCount from Smalltalk
  366. if n is None:
  367. return sorted(self.iteritems(), key=_itemgetter(1), reverse=True)
  368. return _heapq.nlargest(n, self.iteritems(), key=_itemgetter(1))
  369. def elements(self):
  370. '''Iterator over elements repeating each as many times as its count.
  371. >>> c = Counter('ABCABC')
  372. >>> sorted(c.elements())
  373. ['A', 'A', 'B', 'B', 'C', 'C']
  374. # Knuth's example for prime factors of 1836: 2**2 * 3**3 * 17**1
  375. >>> prime_factors = Counter({2: 2, 3: 3, 17: 1})
  376. >>> product = 1
  377. >>> for factor in prime_factors.elements(): # loop over factors
  378. ... product *= factor # and multiply them
  379. >>> product
  380. 1836
  381. Note, if an element's count has been set to zero or is a negative
  382. number, elements() will ignore it.
  383. '''
  384. # Emulate Bag.do from Smalltalk and Multiset.begin from C++.
  385. return _chain.from_iterable(_starmap(_repeat, self.iteritems()))
  386. # Override dict methods where necessary
  387. @classmethod
  388. def fromkeys(cls, iterable, v=None):
  389. # There is no equivalent method for counters because setting v=1
  390. # means that no element can have a count greater than one.
  391. raise NotImplementedError(
  392. 'Counter.fromkeys() is undefined. Use Counter(iterable) instead.')
  393. def update(self, iterable=None, **kwds):
  394. '''Like dict.update() but add counts instead of replacing them.
  395. Source can be an iterable, a dictionary, or another Counter instance.
  396. >>> c = Counter('which')
  397. >>> c.update('witch') # add elements from another iterable
  398. >>> d = Counter('watch')
  399. >>> c.update(d) # add elements from another counter
  400. >>> c['h'] # four 'h' in which, witch, and watch
  401. 4
  402. '''
  403. # The regular dict.update() operation makes no sense here because the
  404. # replace behavior results in the some of original untouched counts
  405. # being mixed-in with all of the other counts for a mismash that
  406. # doesn't have a straight-forward interpretation in most counting
  407. # contexts. Instead, we implement straight-addition. Both the inputs
  408. # and outputs are allowed to contain zero and negative counts.
  409. if iterable is not None:
  410. if isinstance(iterable, Mapping):
  411. if self:
  412. self_get = self.get
  413. for elem, count in iterable.iteritems():
  414. self[elem] = self_get(elem, 0) + count
  415. else:
  416. super(Counter, self).update(iterable) # fast path when counter is empty
  417. else:
  418. self_get = self.get
  419. for elem in iterable:
  420. self[elem] = self_get(elem, 0) + 1
  421. if kwds:
  422. self.update(kwds)
  423. def subtract(self, iterable=None, **kwds):
  424. '''Like dict.update() but subtracts counts instead of replacing them.
  425. Counts can be reduced below zero. Both the inputs and outputs are
  426. allowed to contain zero and negative counts.
  427. Source can be an iterable, a dictionary, or another Counter instance.
  428. >>> c = Counter('which')
  429. >>> c.subtract('witch') # subtract elements from another iterable
  430. >>> c.subtract(Counter('watch')) # subtract elements from another counter
  431. >>> c['h'] # 2 in which, minus 1 in witch, minus 1 in watch
  432. 0
  433. >>> c['w'] # 1 in which, minus 1 in witch, minus 1 in watch
  434. -1
  435. '''
  436. if iterable is not None:
  437. self_get = self.get
  438. if isinstance(iterable, Mapping):
  439. for elem, count in iterable.items():
  440. self[elem] = self_get(elem, 0) - count
  441. else:
  442. for elem in iterable:
  443. self[elem] = self_get(elem, 0) - 1
  444. if kwds:
  445. self.subtract(kwds)
  446. def copy(self):
  447. 'Return a shallow copy.'
  448. return self.__class__(self)
  449. def __reduce__(self):
  450. return self.__class__, (dict(self),)
  451. def __delitem__(self, elem):
  452. 'Like dict.__delitem__() but does not raise KeyError for missing values.'
  453. if elem in self:
  454. super(Counter, self).__delitem__(elem)
  455. def __repr__(self):
  456. if not self:
  457. return '%s()' % self.__class__.__name__
  458. items = ', '.join(map('%r: %r'.__mod__, self.most_common()))
  459. return '%s({%s})' % (self.__class__.__name__, items)
  460. # Multiset-style mathematical operations discussed in:
  461. # Knuth TAOCP Volume II section 4.6.3 exercise 19
  462. # and at http://en.wikipedia.org/wiki/Multiset
  463. #
  464. # Outputs guaranteed to only include positive counts.
  465. #
  466. # To strip negative and zero counts, add-in an empty counter:
  467. # c += Counter()
  468. def __add__(self, other):
  469. '''Add counts from two counters.
  470. >>> Counter('abbb') + Counter('bcc')
  471. Counter({'b': 4, 'c': 2, 'a': 1})
  472. '''
  473. if not isinstance(other, Counter):
  474. return NotImplemented
  475. result = Counter()
  476. for elem, count in self.items():
  477. newcount = count + other[elem]
  478. if newcount > 0:
  479. result[elem] = newcount
  480. for elem, count in other.items():
  481. if elem not in self and count > 0:
  482. result[elem] = count
  483. return result
  484. def __sub__(self, other):
  485. ''' Subtract count, but keep only results with positive counts.
  486. >>> Counter('abbbc') - Counter('bccd')
  487. Counter({'b': 2, 'a': 1})
  488. '''
  489. if not isinstance(other, Counter):
  490. return NotImplemented
  491. result = Counter()
  492. for elem, count in self.items():
  493. newcount = count - other[elem]
  494. if newcount > 0:
  495. result[elem] = newcount
  496. for elem, count in other.items():
  497. if elem not in self and count < 0:
  498. result[elem] = 0 - count
  499. return result
  500. def __or__(self, other):
  501. '''Union is the maximum of value in either of the input counters.
  502. >>> Counter('abbb') | Counter('bcc')
  503. Counter({'b': 3, 'c': 2, 'a': 1})
  504. '''
  505. if not isinstance(other, Counter):
  506. return NotImplemented
  507. result = Counter()
  508. for elem, count in self.items():
  509. other_count = other[elem]
  510. newcount = other_count if count < other_count else count
  511. if newcount > 0:
  512. result[elem] = newcount
  513. for elem, count in other.items():
  514. if elem not in self and count > 0:
  515. result[elem] = count
  516. return result
  517. def __and__(self, other):
  518. ''' Intersection is the minimum of corresponding counts.
  519. >>> Counter('abbb') & Counter('bcc')
  520. Counter({'b': 1})
  521. '''
  522. if not isinstance(other, Counter):
  523. return NotImplemented
  524. result = Counter()
  525. for elem, count in self.items():
  526. other_count = other[elem]
  527. newcount = count if count < other_count else other_count
  528. if newcount > 0:
  529. result[elem] = newcount
  530. return result
  531. if __name__ == '__main__':
  532. # verify that instances can be pickled
  533. from cPickle import loads, dumps
  534. Point = namedtuple('Point', 'x, y', True)
  535. p = Point(x=10, y=20)
  536. assert p == loads(dumps(p))
  537. # test and demonstrate ability to override methods
  538. class Point(namedtuple('Point', 'x y')):
  539. __slots__ = ()
  540. @property
  541. def hypot(self):
  542. return (self.x ** 2 + self.y ** 2) ** 0.5
  543. def __str__(self):
  544. return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)
  545. for p in Point(3, 4), Point(14, 5/7.):
  546. print p
  547. class Point(namedtuple('Point', 'x y')):
  548. 'Point class with optimized _make() and _replace() without error-checking'
  549. __slots__ = ()
  550. _make = classmethod(tuple.__new__)
  551. def _replace(self, _map=map, **kwds):
  552. return self._make(_map(kwds.get, ('x', 'y'), self))
  553. print Point(11, 22)._replace(x=100)
  554. Point3D = namedtuple('Point3D', Point._fields + ('z',))
  555. print Point3D.__doc__
  556. import doctest
  557. TestResults = namedtuple('TestResults', 'failed attempted')
  558. print TestResults(*doctest.testmod())