149. Functional Tools in Python

A brief introduction to Functional Programming in Python.

150. What is Functional Programming?

From Wikipedia::
...Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data. It emphasizes the application of functions, in contrast to the imperative programming style, which emphasizes changes in state.

151. What are Some Functional Languages?

Popular functional programming languages:

  • Scheme
  • Erlang
  • OCaml
  • Haskell
  • F#
  • Clojure
  • R

152. Disclaimer

I do not claim to be a master of functional programming nor is this meant to be a lesson on functional programming. This is meant to be a look into Python features which are related to or insipred by functional programming. While these concepts are handy for all Python programmers and will carry into other functional languages, this is not meant to imply that Python is a great functional language.

You’ll notice Python is not listed as a popular functional langages. Python has functional tools and idioms but is not a purely functional language. If you find think interesting I suggest you consider looking into one of the previously listed functional languages.

153. Who Cares?

Testing and debugging functional programs tend to be easier. There is no global state and the program is a sequence of function calls. Functions tend to be small and compartmentalized. Each function can be tested individually. Debugging involves checking the input and output of each function call.

Functional programming/languages have been growing in popularity in industry and knowing these languages/concepts makes for a rare marketable skill.

Also it’s fun.

154. Some Notes on Function Parameters

There was a mention about not passing an empty list as a default argument. This is not only true of lists but any mutable type such as a set or a dictionary.

This is because the function defaults are evaluated when the function is defined. If the default type is mutable then it can be changed on subsequent function calls.

155. Function Nightmare (nightmare.py)

def simple(x, y=[]):
    y.append(x)
    return y

print simple.func_defaults
print simple(1)
print simple(2)
print simple.func_defaults

156. Getting Rid of Logical Blocks

Small logical blocks can often be removed by making use of Python short circuiting logical statements.

x = 2
x_place = 'did not place'
if x == 1:
    x_place = 'first'
elif x == 2:
    x_place = 'second'
print x_place

157. Short Circuit Example

x = 2
print (x == 1 and 'first') or (x == 2 and 'second') or 'did not place'

158. Using Short Circuit For Good

One of the best uses of this logical evaluation is handling default arguments that you want to be mutable types such as lists or dictionaries.

def simple(x=None):
    x = x or {}
    # Do some more work

159. Getting Rid of Loops

List comprehensions allow you to build loops in place. Simple loops can be removed with list comprehensions. The Python 3.X series supports dictionary and set comprehensions as well.

mix = [1, 'a', 'b', 2, 4]
letters = []
for x in mix:
    if isinstance(mix, basestring):
        letters.append(x)
print letters
print [x for x in mix if isinstance(x, basestring)]

160. Double List Comprehensions (identity.py)

You can have nested list comprehensions to eliminate nested for loops.

def identity(n):
    return [[1 if i == j else 0 for i in xrange(n)] for j in xrange(n)]

print identity(3)

print [(i, j) for i in xrange(3) for j in xrange(3)]

161. Anonymous Functions

So far we have always defined functions using the def keyword. You can also define anonymous (unnamed) functions with lambda.

f = lambda x: x*x # Create a new function and assign to f
print f.__class__
print f(2)

162. More List Filtering

You can also use the filter function. The first argument is the function to be used for the filtering. This could be a defined function or a lambda. The second argument is the iterable. It returns the filtered list.

mix = [1, 'a', 'b', 2, 4]
letters = filter(lambda x: isinstance(x, basestring), mix)
print letters

163. Getting Rid of More Loops

List filtering isn’t the only type of lists you can remove. The reduce function applies a binary function in sequence to an iterable.

Here is a simple total using reduce.

# This is equivalent to ((1 + 2) + 3) + 4)
print reduce(lambda x, y: x + y, [1, 2, 3, 4])
# This is equivalent to ((1 * 2) * 3) * 4) or 4!
print reduce(lambda x, y: x * y, [1, 2, 3, 4])

reduce is built-in to Python 2.X but was moved to the functools module in Python 3.X.

164. Map

map applies a function to each item in an iterable and returns a list of the results.

# Create list of the first four squares
squares = []
for x in [1, 2, 3, 4]:
    squares.append(x**2)
# Becomes
squares = map(lambda x: x**2, [1, 2, 3, 4])

165. Map Example (matrix.py)

text = '1,0,0;0,1,0;0,0,1'
rows = text.split(';')
matrix = []
for row in rows:
    matrix.append([float(x) for x in row.split(',')])
print matrix

matrix = map(lambda x: map(float, x.split(',')), text.split(';'))
print matrix

166. Sorting Lists

The built-in list type has a sort method which sorts the list in place. sort can also take a key parameter which is a function which takes one parameter returns the value which will be used for the sorting. Passing reverse=True will reverse the sort order.

test = [3, 4, 5, 1, 2]
test.sort()
print test
test = [3, 4, 5, 1, 2]
test.sort(key=lambda x: x % 3)
print test

167. More List Sorting Examples (listsort.py)

test = ['C', 'a', 'd', 'B', 'E']
test.sort()
print test # Default alpha sort

test.sort(key=str.lower)
print test # Case insensitive alpha sort

test = [(1, 2), (5, 3), (7, 1), (3, 8)]
test.sort(key=lambda x: x[1])
print test # Sort by second entry

168. Sorting Iterables

list.sort is great but what if you need to sort things that are iterables but not lists. For that you can use the built-in sorted function. The first argument is an iterable and the remaining arguments are the same as sort. Unlike sort which returns None sorted returns the sorted items as a list.

print sorted((8, 6, 7, 5, 3, 0, 9))
print sorted('jenny')
print sorted({'got': 'your number', 'need': 'make you mine'})

169. Common Key Functions

There are some common key functions:

  • Sorting a list of tuples (or nested list) by a given index
  • Sorting by an attribute
  • Sorting by a method

While it isn’t difficult to write these functions you can instead use the operator module.

170. Key Functions Examples (keys.py)

from operator import itemgetter, attrgetter, methodcaller

test = [(1, 2), (5, 3), (7, 1), (3, 8)]
print sorted(test, key=itemgetter(1))

test = ['C', 'a', 'd', 'B', 'E']
print sorted(test, key=methodcaller('lower'))

test = [1 +  1j, 2 - 1j, -1 + 2j]
print sorted(test, key=attrgetter('real'))

171. itertools Module

The itertools module defines functions for commonly used iterators.

  • Lists of integers
  • Cycles of sequences
  • Chains of iterators
  • Permutaions of a sequence
  • Combinations of a sequence

172. itertools Examples (iter.py)

from itertools import chain, takewhile, dropwhile
from itertools import combinations, permutations

a = [1, 2, 3]
b = 'abc'
print [(x, type(x)) for x in chain(a, b)]
print [u''.join(x) for x in combinations(b, 2)]
print [u''.join(x) for x in permutations(b, 2)]
print list(takewhile(lambda x: x % 2 == 1, a))
print list(dropwhile(lambda x: x in 'aeiou', b))

173. Closures

Closures are combination of a function and an environment. They are named closures because they are said to “close over” free variables that are passed when defining the closure.

Since I’m sure none of that made any sense let’s look at some examples.

174. Closure Example (closure.py)

def greater_than_bound(bound):
    def greater(x):
        return x > bound
    return greater

greater_than_ten = greater_than_bound(10)
print greater_than_ten(20)

175. Why Closures?

Why in the world would you want to do this? Somethings are just too complicated for lambda. lambda expressions cannot contain if, for or while statements. Though, as we have seen, many of these statements can be replaced.

176. Up Next

A tour of the Python standard library.