In article <[EMAIL PROTECTED]>, Raymond Hettinger <[EMAIL PROTECTED]> wrote:
> [Diez B. Roggisch] > > > out:) But I wanted a general purpose based solution to be available that > > > doesn't count on len() working on an arbitrary iterable. > > [Peter Otten] > > You show signs of a severe case of morbus itertools. > > I, too, am affected and have not yet fully recovered... > > Maybe you guys were secretly yearning for a magical last element > detector used like this: [...] Although there have already been some nice solutions to this problem, but I'd like to propose another, which mildly abuses some of the newer features of Python It is perhaps not as concise as the previous solutions, nor do I claim it's better; but I thought I'd share it as an alternative approach. Before I affront you with implementation details, here's an example: | from __future__ import with_statement | with last_of(enumerate(file('/etc/passwd', 'rU'))) as fp: | for pos, line in fp: | if fp.marked(): | print "Last line, #%d = %s" % (pos + 1, line.strip()) In short, last_of comprises a trivial context manager that knows how to iterate over its input, and can also indicate that certain elements are "marked". In this case, only the last element is marked. We could also make the truth value of the context manager indicate the marking, as illustrated here: | with last_of("alphabet soup") as final: | for c in final: | if final: | print "Last character: %s" % c This is bit artificial, perhaps, but effective enough. Of course, there is really no reason you have to use "with," since we don't really care what happens when the object goes out of scope: You could just as easily write: | end = last_of(xrange(25)) | for x in end: | if end: | print "Last element: %s" % x But you could also handle nested context, using "with". Happily, the machinery to do all this is both simple and easily generalized to other sorts of "marking" tasks. For example, we could just as well do something special with all the elements that are accepted by a predicate function, e.g., | def isinteger(obj): | return isinstance(obj, (int, long)) | with matching(["a", 1, "b", 2, "c"], isinteger) as m: | for elt in m: | if m.marked(): | print '#%s' % elt, | else: | print '(%s)' % elt, | | print Now that you've seen the examples, here is an implementation. The "marker" class is an abstract base that does most of the work, with the "last_of" and "matching" examples implemented as subclasses: | class marker (object): | """Generate a trivial context manager that flags certain elements | in a sequence or iterable. | | Usage sample: | with marker(ITERABLE) as foo: | for elt in foo: | if foo.marked(): | print 'this is a marked element' | else: | print 'this is an unmarked element' | | Subclass overrides: | .next() -- return the next unconsumed element from the input. | .marked() -- return True iff the last element returned is marked. | | By default, no elements are marked. | """ | def __init__(self, seq): | self._seq = iter(seq) | try: | self._fst = self._seq.next() | except StopIteration: | self.next = self._empty | | def _empty(self): | raise StopIteration | | def _iter(self): | while True: | yield self.next() | | def next(self): | out = self._fst | try: | self._fst = self._seq.next() | except StopIteration: | self.next = self._empty | | return out | | def marked(self): | return False | | def __iter__(self): | return iter(self._iter()) | | def __nonzero__(self): | return self.marked() | | def __enter__(self): | return self | | def __exit__(self, *args): | pass A bit verbose, but uncomplicated apart from the subtlety in handling the end case. Here's last_of, implemented as a subclass: | class last_of (marker): | def __init__(self, seq): | super(last_of, self).__init__(seq) | self._end = False | | def next(self): | out = super(last_of, self).next() | if self.next == self._empty: | self._end = True | | return out | | def marked(self): | return self._end And finally, matching: | class matching (marker): | def __init__(self, seq, func): | super(matching, self).__init__(seq) | self._func = func | self._mark = False | | def next(self): | out = super(matching, self).next() | self._mark = self._func(out) | return out | | def marked(self): | return self._mark Generally speaking, you should only have to override .next() and .marked() to make a useful subclass of marker -- and possibly also __init__ if you need some additional setup. Cheers, -M -- Michael J. Fromberger | Lecturer, Dept. of Computer Science http://www.dartmouth.edu/~sting/ | Dartmouth College, Hanover, NH, USA -- http://mail.python.org/mailman/listinfo/python-list