itertools comments [Was: Re: RELEASED: Python 2.3a2]

Alexander Schmolck a.schmolck at gmx.net
Thu Feb 20 10:53:39 EST 2003


Guido van Rossum <guido at python.org> writes:

> 
> - new module itertools: efficient looping constructs
> 

I've got one general comment on the new itertools (which I think really is a
great addition to the standard library) and a few suggestions for additions.

Wouldn't `xfilter`, `xmap` etc. be a better naming convention than
`ifilter`, `imap` etc? I'd propose this change for 2 reasons:

1) the 'x' prefix is already used for `lazy` constructs (such as xrange and
   xreadlines) which are in pretty much every way equivalent to generators.

2) `i` is currently used to signify `in-place` i.e. a destructive operation,
   (e.g. __iadd__ etc.). It is often useful to have both a destructive and a
   nondestructive variant of a function, sort e.g. would be a prime example
   and I can't think of another reasonable convention in python than to prefix
   i (for in-place). 

   Admittedly, the most "pythonic" approach is to have just a single,
   destructive function that returns `None`, but this is often prohibitively
   incovinient, so that uncountable destructive `sort` functions for lists
   have been written, leading to ambiguity. A convention to distinguishes
   destructive and nondestructive functions at least avoids that, e.g.:

    def isort(l, cmpfunc=None):
        if cmpfunc:
            l.sort(cmpfunc)
        else:
            l.sort()
        return l
    
    def sort(seq, cmpfunc=None):
        return isort(list(seq), cmpfunc=None)


Finally, among the tools I've cooked up myself over time and that aren't
already covered by what's in the new module, I find the following 2
(especially the first) to be the most useful and worthy of addition:

def xgroup(iter,n=2):
    r"""Iterate `n`-wise (default pairwise)  over `iter`.
    Examples:
    >>> list(xgroup(range(9), 3))
    [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
    """
    last = []
    for elt in iter:
        last.append(elt)
        if len(last) == n: yield tuple(last); last = []

def xwindow(iter, n=2, s=1):
    r"""Move an `n`-item (default 2) windows `s` steps (default 1) at a time
    over `iter`.
    
    Examples:
    >>> list(xwindow(range(6),2))
    [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
    """
    
    assert n >= s
    last = []
    for elt in iter:
        last.append(elt)
        if len(last) == n: yield tuple(last); last=last[s:]


A last thing, I think a `cycle` generator would be an really useful idiom to
have, as it provides a much more readable and less errorprone way to achieve
many of the things one normally has to use an awkward modulus construct
for. If I understand the rationale for not providing one correctly, then it is
because the `straightforward` (and non-storage allocating) implementation:

    def xcycle(seq):
        while 1:
            for item in seq: yield item
    
doesn't work as one might expect if `seq` is e.g. an iterator (which BTW sort
of seems to contradict the other reason for not providing it, namely that "the
tool is readily constructible using pure Python" -- it is easy to overlook
this subtleness). Wouldn't the following work, mostly without surprising
overheads?

   def xcycle(seq):
       if not operator.isSequenceType(seq):
           seq = tuple(seq) # or [x for x in seq]
       while 1:
           for item in seq: yield item
   
Since I'd guess that the most common usage scenario would be something like
``cycle([1,2,3])`` (for which there is no suprising storage allocation) I
don't think that that many expectations would be violated, after all.

alex




More information about the Python-list mailing list