_functools.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. """ Supplies the internal functions for functools.py in the standard library """
  2. try: from __pypy__ import builtinify
  3. except ImportError: builtinify = lambda f: f
  4. try: from reprlib import recursive_repr as _recursive_repr
  5. except ImportError: _recursive_repr = lambda: (lambda f: f)
  6. sentinel = object()
  7. @builtinify
  8. def reduce(func, sequence, initial=sentinel):
  9. """reduce(function, sequence[, initial]) -> value
  10. Apply a function of two arguments cumulatively to the items of a sequence,
  11. from left to right, so as to reduce the sequence to a single value.
  12. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
  13. ((((1+2)+3)+4)+5). If initial is present, it is placed before the items
  14. of the sequence in the calculation, and serves as a default when the
  15. sequence is empty."""
  16. iterator = iter(sequence)
  17. if initial is sentinel:
  18. try:
  19. initial = next(iterator)
  20. except StopIteration:
  21. raise TypeError("reduce() of empty sequence with no initial value")
  22. result = initial
  23. for item in iterator:
  24. result = func(result, item)
  25. return result
  26. class partial(object):
  27. """
  28. partial(func, *args, **keywords) - new function with partial application
  29. of the given arguments and keywords.
  30. """
  31. __slots__ = ('_func', '_args', '_keywords', '__dict__')
  32. __module__ = 'functools' # instead of '_functools'
  33. def __init__(*args, **keywords):
  34. if len(args) < 2:
  35. raise TypeError('__init__() takes at least 2 arguments (%d given)'
  36. % len(args))
  37. self, func, args = args[0], args[1], args[2:]
  38. if not callable(func):
  39. raise TypeError("the first argument must be callable")
  40. if isinstance(func, partial):
  41. args = func._args + args
  42. tmpkw = func._keywords.copy()
  43. tmpkw.update(keywords)
  44. keywords = tmpkw
  45. del tmpkw
  46. func = func._func
  47. self._func = func
  48. self._args = args
  49. self._keywords = keywords
  50. def __delattr__(self, key):
  51. if key == '__dict__':
  52. raise TypeError("a partial object's dictionary may not be deleted")
  53. object.__delattr__(self, key)
  54. @property
  55. def func(self):
  56. return self._func
  57. @property
  58. def args(self):
  59. return self._args
  60. @property
  61. def keywords(self):
  62. return self._keywords
  63. def __call__(self, *fargs, **fkeywords):
  64. if self._keywords:
  65. fkeywords = dict(self._keywords, **fkeywords)
  66. return self._func(*(self._args + fargs), **fkeywords)
  67. @_recursive_repr()
  68. def __repr__(self):
  69. cls = type(self)
  70. if cls is partial:
  71. name = 'functools.partial'
  72. else:
  73. name = cls.__name__
  74. tmp = [repr(self.func)]
  75. for arg in self.args:
  76. tmp.append(repr(arg))
  77. if self.keywords:
  78. for k, v in self.keywords.items():
  79. tmp.append("{}={!r}".format(k, v))
  80. return "{}({})".format(name, ', '.join(tmp))
  81. def __reduce__(self):
  82. d = dict((k, v) for k, v in self.__dict__.items() if k not in
  83. ('_func', '_args', '_keywords'))
  84. if len(d) == 0:
  85. d = None
  86. return (type(self), (self._func,),
  87. (self._func, self._args, self._keywords, d))
  88. def __setstate__(self, state):
  89. if not isinstance(state, tuple) or len(state) != 4:
  90. raise TypeError("invalid partial state")
  91. func, args, keywords, d = state
  92. if (not callable(func) or not isinstance(args, tuple) or
  93. (keywords is not None and not isinstance(keywords, dict))):
  94. raise TypeError("invalid partial state")
  95. self._func = func
  96. self._args = tuple(args)
  97. if keywords is None:
  98. keywords = {}
  99. elif type(keywords) is not dict:
  100. keywords = dict(keywords)
  101. self._keywords = keywords
  102. if d is None:
  103. self.__dict__.clear()
  104. else:
  105. self.__dict__.update(d)
  106. @builtinify
  107. def cmp_to_key(mycmp):
  108. """Convert a cmp= function into a key= function"""
  109. class K(object):
  110. __slots__ = ['obj']
  111. def __init__(self, obj):
  112. self.obj = obj
  113. def __lt__(self, other):
  114. return mycmp(self.obj, other.obj) < 0
  115. def __gt__(self, other):
  116. return mycmp(self.obj, other.obj) > 0
  117. def __eq__(self, other):
  118. return mycmp(self.obj, other.obj) == 0
  119. def __le__(self, other):
  120. return mycmp(self.obj, other.obj) <= 0
  121. def __ge__(self, other):
  122. return mycmp(self.obj, other.obj) >= 0
  123. __hash__ = None
  124. return K