itertools.izip brokeness

Tom Anderson twic at urchin.earth.li
Wed Jan 4 06:59:31 EST 2006


On Wed, 4 Jan 2006, Raymond Hettinger wrote:

> rurpy at yahoo.com wrote:
>
>> The whole point of using izip is to make the code shorter, more 
>> concise, and easier to write and understand.
>
> That should be the point of using anything in Python.  The specific goal 
> for izip() was for an iterator version of zip().  Unfortunately, neither 
> tool fits your problem.  At the root of it is the iterator protocol not 
> having an unget() method for pushing back unused elements of the data 
> stream.

An unget() isn't absolutely necessary - another way of doing it would be a 
hasNext() method, as in Java, or a peek(), which gets the next item but 
doesn't advance the iterator.

Here's some code (pardon the old-fashioned functional style in the 
iter_foo methods):

import operator

class xiterable(object):
 	"""This is an entirely abstract class, just to document the
 	xiterable interface.

 	"""
 	def __iter__(self):
 		"""As in the traditional iterable protocol, returns an
 		iterator over this object. Note that this does not have to
 		be an xiterator.

 		"""
 		raise NotImplementedError
 	def __xiter__(self):
 		"""Returns an xiterator over this object.

 		"""
 		raise NotImplementedError

class xiterator(xiterable):
 	"""This is an entirely abstract class, just to document the xiter
 	interface.

 	The xiterable methods should return self.
 	"""
 	def hasNext(self):
 		"""Returns True if calling next would return a value, or
 		False if it would raise StopIteration.

 		"""
 		raise NotImplementedError
 	def next(self):
 		"""As in the traditional iterator protocol.

 		"""
 		raise NotImplementedError
 	def peek(self):
 		"""Returns the value that would be returned by a call to
 		next, but does not advance the iterator - the same value
 		will be returned by the next call to peek or next. If a
 		call to next would raise StopIteration, this method
 		raises StopIteration.

 		"""
 		raise NotImplementedError

def xiter(iterable):
 	if (hasattr(iterable, "__xiter__")):
 		return iterable.__xiter__()
 	else:
 		return xiterwrapper(iter(iterable))

class xiterwrapper(object):
 	def __init__(self, it):
 		self.it = it
 		self.advance()
 	def hasNext(self):
 		return hasattr(self, "_next")
 	def next(self):
 		try:
 			cur = self._next
 			self.advance()
 			return cur
 		except AttributeError:
 			raise StopIteration
 	def peek(self):
 		try:
 			return self._next
 		except AttributeError:
 			raise StopIteration
 	def advance(self):
 		try:
 			self._next = self.it.next()
 		except StopIteration:
 			if (hasattr(self, "_next")):
 				del self._next
 	def __xiter__(self):
 		return self
 	def __iter__(self):
 		return self

def izip_hasnext(*xiters):
 	xiters = map(xiter, xiters)
 	while True:
 		if (reduce(operator.and_, map(hasnext, xiters))):
 			yield tuple(map(getnext, xiters))
 		else:
 			raise StopIteration

def hasnext(xit):
 	return xit.hasNext()

def getnext(it):
 	return it.next()

def izip_peek(*xiters):
 	xiters = map(xiter, xiters)
 	while True:
 		z = tuple(map(peek, xiters))
 		map(advance, xiters)
 		yield z

def peek(xit):
 	return xit.peek()

def advance(xit):
 	return xit.advance()

Anyway, you get the general idea.

tom

-- 
I am the best at what i do.



More information about the Python-list mailing list