1
1
********************************
2
- Functional Programming HOWTO
2
+ Functional programming in Python
3
3
********************************
4
4
5
5
:Author: A. M. Kuchling
6
6
:Release: 0.32
7
7
8
- In this document, we'll take a tour of Python's features suitable for
9
- implementing programs in a functional style. After an introduction to the
10
- concepts of functional programming, we'll look at language features such as
11
- :term: `iterator `\s and :term: `generator `\s and relevant library modules such as
12
- :mod: `itertools ` and :mod: `functools `.
13
8
14
-
15
- Introduction
16
- ============
17
-
18
- This section explains the basic concept of functional programming; if
19
- you're just interested in learning about Python language features,
20
- skip to the next section on :ref: `functional-howto-iterators `.
9
+ The basics of functional programming
10
+ ====================================
21
11
22
12
Programming languages support decomposing problems in several different ways:
23
13
24
- * Most programming languages are **procedural **: programs are lists of
14
+ * Many programming languages are **procedural **: programs are lists of
25
15
instructions that tell the computer what to do with the program's input. C,
26
16
Pascal, and even Unix shells are procedural languages.
27
17
28
18
* In **declarative ** languages, you write a specification that describes the
29
19
problem to be solved, and the language implementation figures out how to
30
- perform the computation efficiently. SQL is the declarative language you're
31
- most likely to be familiar with ; a SQL query describes the data set you want
20
+ perform the computation efficiently. SQL is an example of a declarative
21
+ language ; a SQL query describes the data set you want
32
22
to retrieve, and the SQL engine decides whether to scan tables or use indexes,
33
23
which subclauses should be performed first, etc.
34
24
@@ -57,7 +47,7 @@ functional, for example.
57
47
58
48
In a functional program, input flows through a set of functions. Each function
59
49
operates on its input and produces some output. Functional style discourages
60
- functions with side effects that modify internal state or make other changes
50
+ functions with * side effects * that modify internal state or make other changes
61
51
that aren't visible in the function's return value. Functions that have no side
62
52
effects at all are called **purely functional **. Avoiding side effects means
63
53
not using data structures that get updated as a program runs; every function's
@@ -177,7 +167,7 @@ a few functions specialized for the current task.
177
167
Iterators
178
168
=========
179
169
180
- I'll start by looking at a Python language feature that's an important
170
+ Let's start by considering a Python language feature that's an important
181
171
foundation for writing functional-style programs: iterators.
182
172
183
173
An iterator is an object representing a stream of data; this object returns the
@@ -259,7 +249,6 @@ consume all of the iterator's output, and if you need to do something different
259
249
with the same stream, you'll have to create a new iterator.
260
250
261
251
262
-
263
252
Data Types That Support Iterators
264
253
---------------------------------
265
254
@@ -326,7 +315,6 @@ elements::
326
315
13
327
316
328
317
329
-
330
318
Generator expressions and list comprehensions
331
319
=============================================
332
320
@@ -440,7 +428,7 @@ Generators are a special class of functions that simplify the task of writing
440
428
iterators. Regular functions compute a value and return it, but generators
441
429
return an iterator that returns a stream of values.
442
430
443
- You're doubtless familiar with how regular function calls work in Python or C.
431
+ You may be familiar with how regular function calls work in Python or C.
444
432
When you call a function, it gets a private namespace where its local variables
445
433
are created. When the function reaches a ``return `` statement, the local
446
434
variables are destroyed and the value is returned to the caller. A later call
@@ -529,18 +517,15 @@ Passing values into a generator
529
517
530
518
In Python 2.4 and earlier, generators only produced output. Once a generator's
531
519
code was invoked to create an iterator, there was no way to pass any new
532
- information into the function when its execution is resumed. You could hack
533
- together this ability by making the generator look at a global variable or by
534
- passing in some mutable object that callers then modify, but these approaches
535
- are messy.
520
+ information into the function when its execution is resumed.
536
521
537
- In Python 2.5 there's a simple way to pass values into a generator.
538
- :keyword: ` yield ` became an expression, returning a value that can be assigned to
539
- a variable or otherwise operated on ::
522
+ In Python 2.5, :keyword: ` yield ` became an expression, returning a value that
523
+ can be assigned to a variable or otherwise operated on, providing a simple
524
+ way to pass values into a generator ::
540
525
541
526
val = (yield i)
542
527
543
- I recommend that you **always ** put parentheses around a ``yield `` expression
528
+ It's recommended that you **always ** put parentheses around a ``yield `` expression
544
529
when you're doing something with the returned value, as in the above example.
545
530
The parentheses aren't always necessary, but it's easier to always add them
546
531
instead of having to remember when they're needed.
@@ -608,8 +593,8 @@ generators:
608
593
will also be called by Python's garbage collector when the generator is
609
594
garbage-collected.
610
595
611
- If you need to run cleanup code when a :exc: `GeneratorExit ` occurs, I suggest
612
- using a ``try: ... finally: `` suite instead of catching :exc: `GeneratorExit `.
596
+ If you need to run cleanup code when a :exc: `GeneratorExit ` occurs, it's recommended
597
+ to us a ``try: ... finally: `` suite instead of catching :exc: `GeneratorExit `.
613
598
614
599
The cumulative effect of these changes is to turn generators from one-way
615
600
producers of information into both producers and consumers.
@@ -639,7 +624,7 @@ features of generator expressions:
639
624
>>> [upper(s) for s in [' sentence' , ' fragment' ]]
640
625
['SENTENCE', 'FRAGMENT']
641
626
642
- You can of course achieve the same effect with a list comprehension.
627
+ ( You can achieve the same effect with a list comprehension.)
643
628
644
629
:func: `filter(predicate, iter) <filter> ` returns an iterator over all the
645
630
sequence elements that meet a certain condition, and is similarly duplicated by
@@ -1131,20 +1116,21 @@ usual way::
1131
1116
def print_assign(name, value):
1132
1117
return name + '=' + str(value)
1133
1118
1134
- Which alternative is preferable? That's a style question; my usual course is to
1135
- avoid using ``lambda ``.
1119
+ Which alternative is preferable? That's mostly a question of style.
1136
1120
1137
- One reason for my preference is that ``lambda `` is quite limited in the
1121
+ You may wish to avoid using ``lambda ``, as there are limits to the
1138
1122
functions it can define. The result has to be computable as a single
1139
1123
expression, which means you can't have multiway ``if... elif... else ``
1140
1124
comparisons or ``try... except `` statements. If you try to do too much in a
1141
1125
``lambda `` statement, you'll end up with an overly complicated expression that's
1142
- hard to read. Quick, what's the following code doing? ::
1126
+ hard to read.
1127
+
1128
+ Consider::
1143
1129
1144
1130
import functools
1145
1131
total = functools.reduce(lambda a, b: (0, a[1] + b[1]), items)[1]
1146
1132
1147
- You can figure it out, but it takes time to disentangle the expression to figure
1133
+ It takes some mental effort to disentangle the expression to figure
1148
1134
out what's going on. Using a short nested ``def `` statements makes things a
1149
1135
little bit better::
1150
1136
@@ -1154,7 +1140,7 @@ little bit better::
1154
1140
1155
1141
total = functools.reduce(combine, items)[1]
1156
1142
1157
- But it would be best of all if I had simply used a ``for `` loop::
1143
+ But it would be best of all would have been to use a ``for `` loop::
1158
1144
1159
1145
total = 0
1160
1146
for a, b in items:
@@ -1166,19 +1152,6 @@ Or the :func:`sum` built-in and a generator expression::
1166
1152
1167
1153
Many uses of :func: `functools.reduce ` are clearer when written as ``for `` loops.
1168
1154
1169
- Fredrik Lundh once suggested the following set of rules for refactoring uses of
1170
- ``lambda ``:
1171
-
1172
- 1. Write a lambda function.
1173
- 2. Write a comment explaining what the heck that lambda does.
1174
- 3. Study the comment for a while, and think of a name that captures the essence
1175
- of the comment.
1176
- 4. Convert the lambda to a def statement, using that name.
1177
- 5. Remove the comment.
1178
-
1179
- I really like these rules, but you're free to disagree
1180
- about whether this lambda-free style is better.
1181
-
1182
1155
1183
1156
Revision History and Acknowledgements
1184
1157
=====================================
0 commit comments