Dynamic list filtering with Python

Python is AMAZING!
I was defining a field for a Django model which represents minutes and I thought to allow the selection of ranges of 5 minutes only (ie: 5, 10, 15…), this can be accomplished by passing an iterable of tuples in “choices” param… the point is: how can I pass such collection in the simplest and cleanest manner? The answer I found is: using the powerful, compressed and surprisingly human-readable list filtering feature of python!
And this is the snippet:

[(n, str(n)) for n in range(60) if n % 5 == 0] 

which generates the following list:

[(0, '0'), (5, '5'), (10, '10'), (15, '15'), (20, '20'), (25, '25'), (30, '30'), (35, '35'), (40, '40'), (45, '45'), (50, '50'), (55, '55')]

The snipped can be translated into the following english sentence:

“give me back a list of tuples containing the number and its string conversion for each number divisible by 5 in the numeric range 0-60”

\o/ woah!

UPDATE:

When I wrote this post I was still a python beginner… the snippet can be simplified using only range() in this way:

[(n, str(n)) for n in range(0, 60, 5)]
  • I agree, it’s very readable. What do you think about the same in ruby:

    (0...60).select{|n| n % 5 == 0}.map{|n| [n,n.to_s]}
    
    [[0, "0"], [5, "5"], [10, "10"], [15, "15"], [20, "20"], [25, "25"], [30, "30"], [35, "35"], [40, "40"], [45, "45"], [50, "50"], [55, "55"]]
    
    
  • In my opinion the snippet is more readable in python for 2 reasons: 1. it does not require chaining (method1.method2…) 2. it does not involve particular operators/signs and can be understood without knowing a particular api/language (it is meaningful also to non-programmers, instead the ruby equivalent is more cryptic)

  • I would prefer this: [(n, str(n)) for n in range(0, 60, 5)]

  • This is indeed a better solution! I forgotten the “step” argument of range() ;)