# coding: utf-8
"""
brownie.itools
~~~~~~~~~~~~~~
Implements :mod:`itertools` functions for earlier version of Python.
:copyright: 2010-2011 by Daniel Neuhäuser
:license: BSD, PSF see LICENSE.rst for details
"""
from itertools import repeat, izip
[docs]class chain(object):
"""
An iterator which yields elements from the given `iterables` until each
iterable is exhausted.
.. versionadded:: 0.2
"""
@classmethod
[docs] def from_iterable(cls, iterable):
"""
Alternative constructor which takes an `iterable` yielding iterators,
this can be used to chain an infinite number of iterators.
"""
rv = object.__new__(cls)
rv._init(iterable)
return rv
def __init__(self, *iterables):
self._init(iterables)
def _init(self, iterables):
self.iterables = iter(iterables)
self.current_iterable = iter([])
def __iter__(self):
return self
def next(self):
try:
return self.current_iterable.next()
except StopIteration:
self.current_iterable = iter(self.iterables.next())
return self.current_iterable.next()
[docs]def izip_longest(*iterables, **kwargs):
"""
Make an iterator that aggregates elements from each of the iterables. If
the iterables are of uneven length, missing values are filled-in with
`fillvalue`. Iteration continues until the longest iterable is exhausted.
If one of the iterables is potentially infinite, then the
:func:`izip_longest` function should be wrapped with something that limits
the number of calls (for example :func:`itertools.islice` or
:func:`itertools.takewhile`.) If not specified, `fillvalue` defaults to
``None``.
.. note:: Software and documentation for this function are taken from
CPython, :ref:`license details <psf-license>`.
"""
fillvalue = kwargs.get('fillvalue')
def sentinel(counter=([fillvalue] * (len(iterables) - 1)).pop):
yield counter()
fillers = repeat(fillvalue)
iters = [chain(it, sentinel(), fillers) for it in iterables]
try:
for tup in izip(*iters):
yield tup
except IndexError:
pass
[docs]def permutations(iterable, r=None):
"""
Return successive `r` length permutations of elements in the `iterable`.
If `r` is not specified or is ``None``, then `r` defaults to the length of
the `iterable` and all possible full-length permutations are generated.
Permutations are emitted in lexicographic sort order. So, if the input
`iterable` is sorted, the permutation tuples will be produced in sorted
order.
Elements are treated as unique based on their position, not on their
value. So if the input elements are unique, there will be no repeating
value in each permutation.
The number of items returned is ``n! / (n - r)!`` when ``0 <= r <= n`` or
zero when `r > n`.
.. note:: Software and documentation for this function are taken from
CPython, :ref:`license details <psf-license>`.
"""
pool = tuple(iterable)
n = len(pool)
r = n if r is None else r
for indices in product(range(n), repeat=r):
if len(set(indices)) == r:
yield tuple(pool[i] for i in indices)
[docs]def product(*iterables, **kwargs):
"""
Cartesian product of input iterables.
Equivalent to nested for-loops in a generator expression. For example,
``product(A, B)`` returns the same as ``((x, y) for x in A for y in B)``.
The nested loops cycle like an odometer with the rightmost element
advancing on every iteration. The pattern creates a lexicographic ordering
so that if the input's iterables are sorted, the product tuples are emitted
in sorted order.
To compute the product of an iterable with itself, specify the number of
repetitions with the optional `repeat` keyword argument. For example,
``product(A, repeat=4)`` means the same as ``product(A, A, A, A)``.
.. note:: Software and documentation for this function are taken from
CPython, :ref:`license details <psf-license>`.
"""
pools = map(tuple, iterables) * kwargs.get('repeat', 1)
result = [[]]
for pool in pools:
result = [x + [y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
[docs]def starmap(function, iterable):
"""
Make an iterator that computes the function using arguments obtained from
the iterable. Used instead of :func:`itertools.imap` when an argument
parameters are already grouped in tuples from a single iterable (the data
has been "pre-zipped"). The difference between :func:`itertools.imap` and
:func:`starmap` parallels the distinction between ``function(a, b)`` and
``function(*c)``.
.. note:: Software and documentation for this function are taken from
CPython, :ref:`license details <psf-license>`.
"""
for args in iterable:
yield function(*args)
[docs]def combinations_with_replacement(iterable, r):
"""
Return `r` length sub-sequences of elements from the `iterable` allowing
individual elements to be replaced more than once.
Combinations are emitted in lexicographic sort order. So, if the input
`iterable` is sorted, the combinations tuples will be produced in sorted
order.
Elements are treated as unique based on their position, not on their value.
So if the input elements are unique, the generated combinations will also
be unique.
The number of items returned is ``(n + r - 1)! / r! / (n - 1)!`` when
``n > 0``.
.. note:: Software and documentation for this function are taken from
CPython, :ref:`license details <psf-license>`.
"""
pool = tuple(iterable)
n = len(pool)
for indices in product(xrange(n), repeat=r):
if sorted(indices) == list(indices):
yield tuple(pool[i] for i in indices)
[docs]def compress(data, selectors):
"""
Make an iterator that filters elements from the `data` returning only
those that have a corresponding element in `selectors` that evaluates to
``True``. Stops when either the `data` or `selectors` iterables have been
exhausted.
.. note:: Software and documentation for this function are taken from
CPython, :ref:`license details <psf-license>`.
"""
return (d for d, s in izip(data, selectors) if s)
[docs]def count(start=0, step=1):
"""
Make an iterator that returns evenly spaced values starting with `start`.
Often used as an argument to :func:`imap` to generate consecutive data
points. Also, used with :func:`izip` to add sequence numbers.
When counting with floating point numbers, better accuracy can sometimes
be achieved by substituting multiplicative code such as:
``(start + step * i for i in count())``.
.. note:: Software and documentation for this function are taken from
CPython, :ref:`license details <psf-license>`.
"""
n = start
while True:
yield n
n += step
[docs]def grouped(n, iterable, fillvalue=None):
"""
Groups the items in the given `iterable` to tuples of size `n`. In order
for groups to always be of the size `n` the `fillvalue` is used for
padding.
"""
return izip_longest(fillvalue=fillvalue, *([iter(iterable)] * n))
[docs]def unique(iterable, seen=None):
"""
Yields items from the given `iterable` of (hashable) items, once seen an
item is not yielded again.
:param seen:
An iterable specifying already 'seen' items which will be excluded
from the result.
.. versionadded:: 0.5
.. versionchanged:: 0.5
Items don't have to be hashable any more.
"""
seen = set() if seen is None else set(seen)
seen_unhashable = []
for item in iterable:
try:
if item not in seen:
seen.add(item)
yield item
except TypeError:
if item not in seen_unhashable:
seen_unhashable.append(item)
yield item
[docs]def flatten(iterable, ignore=(basestring, )):
"""
Flattens a nested `iterable`.
:param ignore:
Types of iterable objects which should be yielded as-is.
.. versionadded:: 0.5
"""
stack = [iter(iterable)]
while stack:
try:
item = stack[-1].next()
if isinstance(item, ignore):
yield item
elif isinstance(item, basestring) and len(item) == 1:
yield item
else:
try:
stack.append(iter(item))
except TypeError:
yield item
except StopIteration:
stack.pop()
__all__ = [
'chain', 'izip_longest', 'permutations', 'product', 'starmap',
'combinations_with_replacement', 'compress', 'count', 'grouped', 'unique',
'flatten'
]