Projects Blog About Contact

Examples of beautiful, "Pythonic" code

Feb. 2, 2014

Once you're comfortable with a programming language, and program a bunch of projects in that language, you inevitably aspire to do things idiomatically and take joy in writing beautiful and performant code. Here are some examples when handling sequences, itertools and collections in Python.


Finding a substring:

Use the in keyword instead of working with array indices.

long_string = "This is a very long string"
if "long" in long_string:
    print("Match found")

This looks so much better than longString.indexOf("long") != -1 (WTF?).


Generator expressions:

Here's a pattern I often use to write outputs to a csv file. I'm not sure if I have reinvented a function that is available in a csv processing library.

Here you calculate the formula, convert the result to a string in the csv format and join the strings into the final string in one pass. Finally, the string is saved into a file. Until the resulting string is created, you only use O(1) memory!

from operator import mul
from fractions import Fraction
 
def n_c_k(n, k): 
  return int( reduce(mul, (Fraction(n-i, i+1) for i in range(k)), 1) )
 
 
def binomial_probability(successes, trials, prob_success):
    assert(successes >= 0 and successes <= trials)
    failures = trials - successes
    
    assert(prob_success >= 0 and prob_success <= 1)
    prob_failure = 1 - prob_success
    
    return n_c_k(trials, successes) * pow(prob_success, successes) * pow(prob_failure, failures)
 
 
def binomial_distribution(trials, prob_success):
    values = ("{},{}".format(i, binomial_probability(i, trials, prob_success)) for i in range(trials + 1))
 
    csv_values = "\n".join(values)
 
    file_name = "binomial_distribution_{}_{}.csv".format(trials, prob_success)
    with open(file_name, "w") as f:
        f.write(csv_values)

Counters

Let's say you have a list with repeated values and you want to count the occurrence of each value. If you come from a background in low-level languages, you'll probably write a for loop. But this use case is so common that the Python standard library has Counters.

>>> from collections import Counter
>>> fruits = ['orange', 'banana', 'apple', 'orange', 'banana']
>>> Counter(fruits)
Counter({'orange': 2, 'banana': 2, 'apple': 1})

Enumerate when looping to get both the index and value of items in a list.

x = ['a', 'b', 'c']
for index, item in enumerate(x):
    print(index, item)
 
0 a
1 b
2 c

import X from Y as Z

Let's say you have a class/function with a verbose name in module A that needs to be imported into module B. Or A has a function called filter_items and you need the same name in B and still import A's filter_items. You can rename the class/function within B with ease and still have readable code. You don't have to monkeypatch A.

#A.py
 
def filter_items(items):
    for i in items:
        if i < 10:
            yield i
 
 
#B.py
 
from A import filter_items as A_filter_items
 
def filter_items(items):
    for i in items:
        if i <= 5:
            yield i
 
def do_something(items):
    x = A_filter_items(items)
    y = filter_items(items)
    return (x, y)

Argument unpacking

def add(one, two):
    return one + two
 
my_list = [1, 2]
x = add(*my_list)  # x = 3
 
my_dict = {"one": 1, "two": 2}
y = add(**my_dict) #y = 3

Looping through multiple sequences using zip (zip_longest in python3)

>>> from itertools import zip_longest
>>> x = [1, 2, 3, 4]
>>> y = ['a', 'b', 'c']
>>> for i, j in zip_longest(x, y):
...     print(i, j)
... 
1 a
2 b
3 c
4 None

Dictionary comprehensions:

Let's build on the previous example.

>>> my_dict = {key: value for key, value in zip_longest(x,y)}
>>> my_dict
{1: 'a', 2: 'b', 3: 'c', 4: None}