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.
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.