Backwards compatibility [was Re: is parameter an iterable?]

Tom Anderson twic at urchin.earth.li
Tue Nov 22 10:54:17 EST 2005


On Tue, 22 Nov 2005, Steven D'Aprano wrote:

> Are there practical idioms for solving the metaproblem "solve problem X 
> using the latest features where available, otherwise fall back on older, 
> less powerful features"?
>
> For instance, perhaps I might do this:
>
> try:
>    built_in_feature
> except NameError:
>    # fall back on a work-around
>    from backwards_compatibility import \
>    feature as built_in_feature
>
> Do people do this or is it a bad idea?

>From some code i wrote yesterday, which has to run under 2.2:

try:
 	True
except NameError:
 	True = 1 == 1
 	False = 1 == 0

Great minds think alike!

As for whether it's a bad idea, well, bad or not, it certainly seems like 
the least worst.

> Are there other techniques to use? Obviously refusing to run is a 
> solution (for some meaning of "solution"), it may even be a practical 
> solution for some cases, but is it the only one?

How about detecting which environment you're in, then running one of two 
entirely different sets of code? Rather than trying to construct modern 
features in the antique environment, write code for each, using the local 
idioms. The trouble with this is that you end up with massive duplication; 
you can try to factor out the common parts, but i suspect that the 
differing parts will be a very large fraction of the codebase.

> If I have to write code that can't rely on iter() existing in the 
> language, what should I do?

Can you implement your own iter()? I have no idea what python 2.0 was 
like, but would something like this work:

class _iterator:
 	def __init__(self, x):
 		self.x = x
 		self.j = 0
 	def next(self):
 		self.j = self.j + 1
 		return self.x.next()
 	def __getitem__(self, i):
 		if (i != self.j):
 			raise ValueError, "out of order iteration"
 		try:
 			return self.next()
 		except StopIteration:
 			raise IndexError
 	def __iter__(self):
 		return self
 	# hopefully, we don't need this, but if we do ...
 	def __len__(self):
 		return sys.maxint # and rely on StopIteration to stop the loop

class _listiterator(_iterator):
 	def next(self):
 		try:
 			item = self.x[self.j]
 			self.j = self.j + 1
 			return item
 		except IndexError:
 			raise StopIteration
 	def __getitem__(self, i):
 		if (i != self.j):
 			raise ValueError, "out of order iteration"
 		self.j = self.j + 1
 		return self.x[i]

import types

def iter(x):
 	# if there's no hasattr, use explicit access and try-except blocks
 	# handle iterators and iterables from the future
 	if hasattr(x, "__iter__"):
 		return _iterator(x.__iter__())
 	# if there's no __getitem__ on lists, try x[0] and catch the exception
 	# but leave the __getitem__ test to catch objects from the future
 	if hasattr(x, "__getitem__"):
 		return _listiterator(x)
 	if type(x) == types.FileType:
 		return _fileiterator(x) # you can imagine the implementation of this
 	# insert more tests for specific types here as you like
 	raise TypeError, "iteration over non-sequence"

?

NB haven't actually tried to run that code.

tom

-- 
I'm angry, but not Milk and Cheese angry. -- Mike Froggatt



More information about the Python-list mailing list