Python allows you to write code in a variety of styles: procedural, object oriented, and functional. What is functional programming and why should you care?
What is functional programming?
Functional programming is a declarative style of programming that tries to minimize side effects and the use of variables. You might already be doing functional programming without realizing it.
- Have you ever written an SQL query for a database?
- Have you ever piped a series of commands in a Linux shell?
If so, you've done functional programming.
A Python example
Linux has a "wc" command that counts lines, words, and characters in a text file. Let's compare two versions, one written in a procedural style and the other in a functional style.
Procedural wc
def wc_1(filename):
'''
Description:
1. initialize some counter variables
2. loop through a file line by line
3. for each line, increment the counters
'''
line_count = 0
word_count = 0
char_count = 0
with open(filename, 'r') as f:
for line in f:
line_count += 1
char_count += len(line)
word_count += len(line.split())
return (line_count, word_count, char_count)
Functional wc
def wc_2(filename):
'''
Description:
1. read the file into a list
2. get the total number of lines
3. sum the number of characters in each line
4. sum the number of words in each line
'''
with open(filename, 'r') as f:
lines = f.readlines()
line_count = len(lines)
char_count = sum(len(line) for line in lines)
word_count = sum(len(line.split()) for line in lines)
return (line_count, word_count, char_count)
Comparing the two versions
In the procedural version
- you have to create 3 separate counter variables
- you have to initialize them correctly
- you have to update the counts for each line in the file
In the functional version
- there are no counter variables
- you let Python's sum() function tally the totals
I prefer the functional version. I can say "sum the number of characters in each line" or "sum the number of words in each line" and Python knows how to do that. And I know that Python won't make any stupid mistakes like forgetting to initialize a variable or increasing the count by the wrong amount.
Performance
The profiler shows us that performance is about the same for both versions. So with this particular input file there's no performance penalty for reading the entire file into memory.
prun wc_1('python_kwic.txt') 6046 function calls in 0.020 seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 1 0.011 0.011 0.020 0.020 wc.py:21(wc_1) 2014 0.004 0.000 0.004 0.000 {method 'split' of 'str' objects} 4028 0.004 0.000 0.004 0.000 {len} 1 0.001 0.001 0.001 0.001 {open} 1 0.000 0.000 0.020 0.020 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
prun wc_2('python_kwic.txt') 6050 function calls in 0.017 seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 1 0.008 0.008 0.017 0.017 wc.py:1(wc_2) 4029 0.004 0.000 0.004 0.000 {len} 2014 0.004 0.000 0.004 0.000 {method 'split' of 'str' objects} 1 0.001 0.001 0.001 0.001 {method 'readlines' of 'file' objects} 1 0.001 0.001 0.001 0.001 {open} 2 0.000 0.000 0.000 0.000 {sum} 1 0.000 0.000 0.017 0.017 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Additional Resources
Functional Programming HOWTO --
https://docs.python.org/2/howto/functional.html
Functional Programming in Python --
http://www.oreilly.com/programming/free/functional-programming-python.csp