Programmers, Get Familiar with ‘Pythonic’ Python

Now in its 26th year (and still going strong in terms of popularity), Python stands out for its distinctive syntax compared to those programming languages based on C. Programmers appreciate how Python means not having types; and using four-space indents makes it much easier to identifying blocks. However, there’s much more to Python than just boosted readability.

The term “pythonic” is a bit vague; I like to think of it as describing Python code that does things the “Python way.” It’s analogous in many ways to carpentry: go against the grain, and you’ll have a much harder time getting the wood to do what you want. In essence, “pythonic” means simple, easy to read, and equally easy to comprehend. (If you want a taste of the online debate over the term, check out this short thread on Stack Overflow.)

Loops and arrays?

Let’s start with a simple example. Programmers who come from other programming languages such as C and C++ are used to loops and arrays. If you want to sum up the elements of an array, here’s a simple program in C99:

	int total = 0;
	int values[10] = { 0,5,8 };

	for (int i = 0; i < 10; i++) {
		total += values[i];
	}

 

What would that look like in Python?

from array import array

a = array("i",[8,5,0])

total = 0
for val in a:
  total += val
print(total)

 

It’s OK but not exactly pythonic, as it uses an array (and unless you don’t have a choice, you should always use lists in place of arrays). A pythonic version, with a list, might look like the below:

print(sum([8,5,0]))

 

See how much simpler that is? Having a built in function like sum() helps; there are also lambdas, which we’ll come to shortly.

Generators

Generators are one of Python’s most powerful features. A generator is a function that returns the next value every time the function is called (it’s similar to the C# yield statement).

Take this simple program to sum up the first n numbers. It defines a function nextnums(n), which builds up a list of numbers. The built-in sum function takes that list and adds everything up:

def nextnums(n):
    num, listnum = 0, []
    while num < n:
        listnum.append(num)
        num += 1
    return listnum
    
print( sum(nextnums(100)))

 

Well, that works—but it’s quite inefficient, as it has to build a list in memory.

If you wanted to write the nextnums(n) function as an iterator, it might look like the below. The yield statement returns one number: 0 on the first call, then 1 on the next.

   def nextnums(n):
       num = 0
       while num < n:
           yield num
           num += 1

 

Thankfully, there’s a built-in function range() that handles this functionality. In Python 2, it builds up a list of numbers; in Python 3, it just returns an iterator like yield). I upped the high value to 100 million:

print( sum(range(0,100000000)))

 

On Ubuntu 17, the Python 3 version runs this in 1.88 seconds; the Python 2 one takes 2.76 seconds. And the Python 3 one doesn’t need to build a list!

Lambdas

A lambda is just a fancy name for a function without a name. Say you have a for-loop in C/C++ that calls a function. You might translate it to do the same in Python; with lambdas, you can make such an operation even simpler:

f = lambda a, b : a * a + b
print(f(2,3))

 

That outputs 7.

It’s as if you defined a function and assigned it to the variable f. Functions are first-class citizens in Python, so you can pass them as parameters, return them from functions, assign them to variables, and store them in lists and dictionaries.

Python has map and reduce functions, which let you use a functional programming style. Say we have a list of items, and would like to have every item in the list multiplied by 3. The map function takes a function and applies it to every item in the list (or other types of sequence, i.e., sets or dictionaries). It’s a whole for-loop in a single function call:

mylist = [6, 7, 8, 9, 10]
print(list(map(lambda x: x * 3, mylist)))

 

The first parameter of map is the function to call. Here, I used a lambda that multiplies an item by 3. The second parameter is the sequence—that’s the five numbers in my list. As map returns a map object (whatever that is), you need to perform list( to that map object to get your list. Not bad for one line!

Using List Comprehensions

In some of the earlier examples, I used lists, building them up with simple assignments such as:

mylist = [6, 7, 8, 9, 10].

 

List comprehensions let you construct lists, albeit much more powerfully. So how about constructing a list of primes up to 50, using just two lines? The following example comes from this webpage on list comprehensions:

noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)]
primes = [x for x in range(2, 50) if x not in noprimes]
print(primes)

 

That outputs: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47].

The first line builds a list of numbers that are not primes. It does two loops: I from 2 to 8, and then j from I-squared up to 50, in increments of i. The second list just returns all the numbers between 2 and 50 that are not in the first list. It’s elegant and pythonic.

Condition Expressions

Some languages—e.g., C, C++, C#—all support an assignment using the ?: syntax. Like this:

x = (x >50) ? 50 : 25; 

 

If x is above 50, it gets the value 50; otherwise, it gets the value 25.
Python supports this, but with slightly different syntax. That assignment in Python is this:

 x = 50 (if x > 50 else 25)

 

The brackets are optional, but considered a good thing. The decision for this methodology, made in 2003  (PEP 308), was a BDFL best judgment call (BDFL is Guido von Rossum, Python’s creator).

Conclusion

Learning to write code in a pythonic manner is something to aspire to. If you haven’t already, check out the style guide for Python, PEP8, written by Guido van Rossum and others. It features a lot of good advice, including one mention of pythonic code guidelines. (One note of caution: I do think it’s possible to go overboard, however, and write some horribly incomprehensible code in very few lines; make sure your code is truly elegant, not just short.)

Related