Mathematics, philosophy, programming, in-line skating and everything in between. More about me…

My Blog

My Latest Tweets

Follow me on Twitter…
English | Czech
Choose your language. I write in English, but I translate most of my articles to Czech as well. Zvolte si jazyk. Píšu anglicky, ale většinu svých článků překládám i do češtiny.

Three Drops of Python

The more I get to know Python the more I like it. In this post I am going to share three code fragments that I wrote in the past few months. Whether useful or not, they demonstrate the beauty of the language that I admire. No doubt my code could be improved. I’ll be grateful for any suggestions.

Float Ranges

The built-in range and xrange functions are perfect for generating any list of integers you may dream of. I was only wondering how do you make a list of floats should you need it. I came up with the following solution:

def fRange(startOrStop, stop = None, step = 0.1):
	if stop is None:
		start, stop = 0, startOrStop
	else:
		start = startOrStop
	result = []
	while (start < stop) if step > 0 else (start > stop):
		result.append(start)
		start += step
	return result


def xfRange(startOrStop, stop = None, step = 0.1):
	if stop is None:
		start, stop = 0, startOrStop
	else:
		start = startOrStop
	while (start < stop) if step > 0 else (start > stop):
		yield start
		start += step

The xfRange function should emulate xrange, that is, it should have constant memory requirements. I love the concept of generators in Python :-).

The functions can be easily tested:

for args in ((0.5,), (0.05,), (5, 5.35), (10, 30, 5.33), (7, 6, -0.4), (100, 0, -23.56)):
	print args
	print fRange(*args)
	print [ x for x in xfRange(*args) ]
	print

Apart from the usual float inaccuracies, the output looks as expected.

(0.5,)
[0, 0.10000000000000001, 0.20000000000000001, 0.30000000000000004, 0.40000000000000002]
[0, 0.10000000000000001, 0.20000000000000001, 0.30000000000000004, 0.40000000000000002]

(0.050000000000000003,)
[0]
[0]

(5, 5.3499999999999996)
[5, 5.0999999999999996, 5.1999999999999993, 5.2999999999999989]
[5, 5.0999999999999996, 5.1999999999999993, 5.2999999999999989]

(10, 30, 5.3300000000000001)
[10, 15.33, 20.66, 25.990000000000002]
[10, 15.33, 20.66, 25.990000000000002]

(7, 6, -0.40000000000000002)
[7, 6.5999999999999996, 6.1999999999999993]
[7, 6.5999999999999996, 6.1999999999999993]

(100, 0, -23.559999999999999)
[100, 76.439999999999998, 52.879999999999995, 29.319999999999997, 5.759999999999998]
[100, 76.439999999999998, 52.879999999999995, 29.319999999999997, 5.759999999999998]

Selecting Unique Items from Several Lists

Imagine we have several lists of arbitrary items, and we want to get all unique items that appear in the lists. Simple concatenation of the lists would not remove duplicate items:

a = [3, 5, 2]
b = [-4, -6, 5]
c = [-4, -4]

print a + b + c # prints [3, 5, 2, -4, -6, 5, -4, -4]

I found a cool solution using the set type. The lists are cumulatively merged together as sets, then converted back to a single list.

def unique(*lists):
	return list(reduce(lambda x, y: x.union(y), lists, set()))

A simple test case:

a = [3, 5, 2]
b = [-4, -6, 5]
c = [-4, -4]

print unique(a, b, c)
print unique(a, b)
print unique(a, c)
print unique(c)

The output. Note that the function works for a single list as well.

[-6, 3, -4, 2, 5]
[-6, 2, 3, -4, 5]
[2, 3, -4, 5]
[-4]

Sorting String Dates

Suppose we have a list of dates in this format ("%a %D" in strftime speak):

dates = ['Thu 01/29/09', 'Wed 01/28/09', 'Fri 12/05/08', 'Mon 02/02/09', 'Sat 11/08/08', 'Fri 09/19/08']

If we wanted to put them in order, we could use the standard sorted function or the sort method of the list. We just need to supply our own comparison function since the built-in cmp uses lexicographical ordering which wouldn’t work out in this case. My take at the comparison function:

def dateCmp(a, b):
	a = a[4:].split('/') # 'Sun 02/01/09' -> '02/01/09' -> ['02', '01', '09']
	b = b[4:].split('/')
	return cmp(a[2], b[2]) or cmp(a[0], b[0]) or cmp(a[1], b[1]) # first years, then months, then days

The short-circuit nature of or makes the comparison very simple. Days are compared only when the months are the same, and months in turn are compared only when the years are identical.

The following test program…

print sorted(dates)
print sorted(dates, cmp = dateCmp)
print sorted(dates, cmp = dateCmp, reverse = True)

…produces this output:

['Fri 09/19/08', 'Fri 12/05/08', 'Mon 02/02/09', 'Sat 11/08/08', 'Thu 01/29/09', 'Wed 01/28/09']
['Fri 09/19/08', 'Sat 11/08/08', 'Fri 12/05/08', 'Wed 01/28/09', 'Thu 01/29/09', 'Mon 02/02/09']
['Mon 02/02/09', 'Thu 01/29/09', 'Wed 01/28/09', 'Fri 12/05/08', 'Sat 11/08/08', 'Fri 09/19/08']

More generic solution would be to convert the dates to timestamps (I’d recommend the dateutil.parser module) and sort them numerically. However, the above solution served me well in a quick’n’dirty program for which date parsing would be something of an overkill.

February 17, MMIX — Programming and Python.

Speak your mind

Allowed HTML tags are a, blockquote, em, code, li, ol, p, pre, strong, ul. Links to other comments in the form “[IV]” or “[4]” are detected automatically.