Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4216d2d

Browse files
committed
#4378: fix a few functional HOWTO 2.xisms.
1 parent 7a0f747 commit 4216d2d

1 file changed

Lines changed: 153 additions & 156 deletions

File tree

Doc/howto/functional.rst

Lines changed: 153 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -659,54 +659,6 @@ This can also be written as a list comprehension:
659659
>>> list(x for x in range(10) if is_even(x))
660660
[0, 2, 4, 6, 8]
661661

662-
``functools.reduce(func, iter, [initial_value])`` cumulatively performs an
663-
operation on all the iterable's elements and, therefore, can't be applied to
664-
infinite iterables. (Note it is not in :mod:`builtins`, but in the
665-
:mod:`functools` module.) ``func`` must be a function that takes two elements
666-
and returns a single value. :func:`functools.reduce` takes the first two
667-
elements A and B returned by the iterator and calculates ``func(A, B)``. It
668-
then requests the third element, C, calculates ``func(func(A, B), C)``, combines
669-
this result with the fourth element returned, and continues until the iterable
670-
is exhausted. If the iterable returns no values at all, a :exc:`TypeError`
671-
exception is raised. If the initial value is supplied, it's used as a starting
672-
point and ``func(initial_value, A)`` is the first calculation. ::
673-
674-
>>> import operator, functools
675-
>>> functools.reduce(operator.concat, ['A', 'BB', 'C'])
676-
'ABBC'
677-
>>> functools.reduce(operator.concat, [])
678-
Traceback (most recent call last):
679-
...
680-
TypeError: reduce() of empty sequence with no initial value
681-
>>> functools.reduce(operator.mul, [1,2,3], 1)
682-
6
683-
>>> functools.reduce(operator.mul, [], 1)
684-
1
685-
686-
If you use :func:`operator.add` with :func:`functools.reduce`, you'll add up all the
687-
elements of the iterable. This case is so common that there's a special
688-
built-in called :func:`sum` to compute it:
689-
690-
>>> import functools
691-
>>> functools.reduce(operator.add, [1,2,3,4], 0)
692-
10
693-
>>> sum([1,2,3,4])
694-
10
695-
>>> sum([])
696-
0
697-
698-
For many uses of :func:`functools.reduce`, though, it can be clearer to just write the
699-
obvious :keyword:`for` loop::
700-
701-
import functools
702-
# Instead of:
703-
product = functools.reduce(operator.mul, [1,2,3], 1)
704-
705-
# You can write:
706-
product = 1
707-
for i in [1,2,3]:
708-
product *= i
709-
710662

711663
``enumerate(iter)`` counts off the elements in the iterable, returning 2-tuples
712664
containing the count and each element. ::
@@ -744,6 +696,7 @@ the constructed list's ``.sort()`` method. ::
744696
(For a more detailed discussion of sorting, see the Sorting mini-HOWTO in the
745697
Python wiki at http://wiki.python.org/moin/HowTo/Sorting.)
746698

699+
747700
The ``any(iter)`` and ``all(iter)`` built-ins look at the truth values of an
748701
iterable's contents. :func:`any` returns True if any element in the iterable is
749702
a true value, and :func:`all` returns True if all of the elements are true
@@ -763,90 +716,27 @@ values:
763716
True
764717

765718

766-
Small functions and the lambda expression
767-
=========================================
768-
769-
When writing functional-style programs, you'll often need little functions that
770-
act as predicates or that combine elements in some way.
771-
772-
If there's a Python built-in or a module function that's suitable, you don't
773-
need to define a new function at all::
774-
775-
stripped_lines = [line.strip() for line in lines]
776-
existing_files = filter(os.path.exists, file_list)
777-
778-
If the function you need doesn't exist, you need to write it. One way to write
779-
small functions is to use the ``lambda`` statement. ``lambda`` takes a number
780-
of parameters and an expression combining these parameters, and creates a small
781-
function that returns the value of the expression::
782-
783-
lowercase = lambda x: x.lower()
784-
785-
print_assign = lambda name, value: name + '=' + str(value)
786-
787-
adder = lambda x, y: x+y
788-
789-
An alternative is to just use the ``def`` statement and define a function in the
790-
usual way::
791-
792-
def lowercase(x):
793-
return x.lower()
794-
795-
def print_assign(name, value):
796-
return name + '=' + str(value)
797-
798-
def adder(x,y):
799-
return x + y
800-
801-
Which alternative is preferable? That's a style question; my usual course is to
802-
avoid using ``lambda``.
803-
804-
One reason for my preference is that ``lambda`` is quite limited in the
805-
functions it can define. The result has to be computable as a single
806-
expression, which means you can't have multiway ``if... elif... else``
807-
comparisons or ``try... except`` statements. If you try to do too much in a
808-
``lambda`` statement, you'll end up with an overly complicated expression that's
809-
hard to read. Quick, what's the following code doing?
810-
811-
::
812-
813-
import functools
814-
total = functools.reduce(lambda a, b: (0, a[1] + b[1]), items)[1]
815-
816-
You can figure it out, but it takes time to disentangle the expression to figure
817-
out what's going on. Using a short nested ``def`` statements makes things a
818-
little bit better::
819-
820-
import functools
821-
def combine (a, b):
822-
return 0, a[1] + b[1]
823-
824-
total = functools.reduce(combine, items)[1]
825-
826-
But it would be best of all if I had simply used a ``for`` loop::
827-
828-
total = 0
829-
for a, b in items:
830-
total += b
831-
832-
Or the :func:`sum` built-in and a generator expression::
719+
``zip(iterA, iterB, ...)`` takes one element from each iterable and
720+
returns them in a tuple::
833721

834-
total = sum(b for a,b in items)
722+
zip(['a', 'b', 'c'], (1, 2, 3)) =>
723+
('a', 1), ('b', 2), ('c', 3)
835724

836-
Many uses of :func:`functools.reduce` are clearer when written as ``for`` loops.
725+
It doesn't construct an in-memory list and exhaust all the input iterators
726+
before returning; instead tuples are constructed and returned only if they're
727+
requested. (The technical term for this behaviour is `lazy evaluation
728+
<http://en.wikipedia.org/wiki/Lazy_evaluation>`__.)
837729

838-
Fredrik Lundh once suggested the following set of rules for refactoring uses of
839-
``lambda``:
730+
This iterator is intended to be used with iterables that are all of the same
731+
length. If the iterables are of different lengths, the resulting stream will be
732+
the same length as the shortest iterable. ::
840733

841-
1) Write a lambda function.
842-
2) Write a comment explaining what the heck that lambda does.
843-
3) Study the comment for a while, and think of a name that captures the essence
844-
of the comment.
845-
4) Convert the lambda to a def statement, using that name.
846-
5) Remove the comment.
734+
zip(['a', 'b'], (1, 2, 3)) =>
735+
('a', 1), ('b', 2)
847736

848-
I really like these rules, but you're free to disagree
849-
about whether this lambda-free style is better.
737+
You should avoid doing this, though, because an element may be taken from the
738+
longer iterators and discarded. This means you can't go on to use the iterators
739+
further because you risk skipping a discarded element.
850740

851741

852742
The itertools module
@@ -896,29 +786,6 @@ of the second, and so on, until all of the iterables have been exhausted. ::
896786
itertools.chain(['a', 'b', 'c'], (1, 2, 3)) =>
897787
a, b, c, 1, 2, 3
898788

899-
``itertools.izip(iterA, iterB, ...)`` takes one element from each iterable and
900-
returns them in a tuple::
901-
902-
itertools.izip(['a', 'b', 'c'], (1, 2, 3)) =>
903-
('a', 1), ('b', 2), ('c', 3)
904-
905-
It's similar to the built-in :func:`zip` function, but doesn't construct an
906-
in-memory list and exhaust all the input iterators before returning; instead
907-
tuples are constructed and returned only if they're requested. (The technical
908-
term for this behaviour is `lazy evaluation
909-
<http://en.wikipedia.org/wiki/Lazy_evaluation>`__.)
910-
911-
This iterator is intended to be used with iterables that are all of the same
912-
length. If the iterables are of different lengths, the resulting stream will be
913-
the same length as the shortest iterable. ::
914-
915-
itertools.izip(['a', 'b'], (1, 2, 3)) =>
916-
('a', 1), ('b', 2)
917-
918-
You should avoid doing this, though, because an element may be taken from the
919-
longer iterators and discarded. This means you can't go on to use the iterators
920-
further because you risk skipping a discarded element.
921-
922789
``itertools.islice(iter, [start], stop, [step])`` returns a stream that's a
923790
slice of the iterator. With a single ``stop`` argument, it will return the
924791
first ``stop`` elements. If you supply a starting index, you'll get
@@ -953,8 +820,6 @@ consumed more than the others. ::
953820
Calling functions on elements
954821
-----------------------------
955822

956-
``itertools.imap(func, iter)`` is the same as built-in :func:`map`.
957-
958823
The ``operator`` module contains a set of functions corresponding to Python's
959824
operators. Some examples are ``operator.add(a, b)`` (adds two values),
960825
``operator.ne(a, b)`` (same as ``a!=b``), and ``operator.attrgetter('id')``
@@ -976,12 +841,10 @@ Selecting elements
976841
Another group of functions chooses a subset of an iterator's elements based on a
977842
predicate.
978843

979-
``itertools.ifilter(predicate, iter)`` is the same as built-in :func:`filter`.
980-
981-
``itertools.ifilterfalse(predicate, iter)`` is the opposite, returning all
844+
``itertools.filterfalse(predicate, iter)`` is the opposite, returning all
982845
elements for which the predicate returns false::
983846

984-
itertools.ifilterfalse(is_even, itertools.count()) =>
847+
itertools.filterfalse(is_even, itertools.count()) =>
985848
1, 3, 5, 7, 9, 11, 13, 15, ...
986849

987850
``itertools.takewhile(predicate, iter)`` returns elements for as long as the
@@ -1083,6 +946,54 @@ Here's a small but realistic example::
1083946
server_log = functools.partial(log, subsystem='server')
1084947
server_log('Unable to open socket')
1085948

949+
``functools.reduce(func, iter, [initial_value])`` cumulatively performs an
950+
operation on all the iterable's elements and, therefore, can't be applied to
951+
infinite iterables. (Note it is not in :mod:`builtins`, but in the
952+
:mod:`functools` module.) ``func`` must be a function that takes two elements
953+
and returns a single value. :func:`functools.reduce` takes the first two
954+
elements A and B returned by the iterator and calculates ``func(A, B)``. It
955+
then requests the third element, C, calculates ``func(func(A, B), C)``, combines
956+
this result with the fourth element returned, and continues until the iterable
957+
is exhausted. If the iterable returns no values at all, a :exc:`TypeError`
958+
exception is raised. If the initial value is supplied, it's used as a starting
959+
point and ``func(initial_value, A)`` is the first calculation. ::
960+
961+
>>> import operator, functools
962+
>>> functools.reduce(operator.concat, ['A', 'BB', 'C'])
963+
'ABBC'
964+
>>> functools.reduce(operator.concat, [])
965+
Traceback (most recent call last):
966+
...
967+
TypeError: reduce() of empty sequence with no initial value
968+
>>> functools.reduce(operator.mul, [1,2,3], 1)
969+
6
970+
>>> functools.reduce(operator.mul, [], 1)
971+
1
972+
973+
If you use :func:`operator.add` with :func:`functools.reduce`, you'll add up all the
974+
elements of the iterable. This case is so common that there's a special
975+
built-in called :func:`sum` to compute it:
976+
977+
>>> import functools
978+
>>> functools.reduce(operator.add, [1,2,3,4], 0)
979+
10
980+
>>> sum([1,2,3,4])
981+
10
982+
>>> sum([])
983+
0
984+
985+
For many uses of :func:`functools.reduce`, though, it can be clearer to just write the
986+
obvious :keyword:`for` loop::
987+
988+
import functools
989+
# Instead of:
990+
product = functools.reduce(operator.mul, [1,2,3], 1)
991+
992+
# You can write:
993+
product = 1
994+
for i in [1,2,3]:
995+
product *= i
996+
1086997

1087998
The operator module
1088999
-------------------
@@ -1232,6 +1143,92 @@ idiom::
12321143
join = partial(foldl, concat, "")
12331144

12341145

1146+
Small functions and the lambda expression
1147+
=========================================
1148+
1149+
When writing functional-style programs, you'll often need little functions that
1150+
act as predicates or that combine elements in some way.
1151+
1152+
If there's a Python built-in or a module function that's suitable, you don't
1153+
need to define a new function at all::
1154+
1155+
stripped_lines = [line.strip() for line in lines]
1156+
existing_files = filter(os.path.exists, file_list)
1157+
1158+
If the function you need doesn't exist, you need to write it. One way to write
1159+
small functions is to use the ``lambda`` statement. ``lambda`` takes a number
1160+
of parameters and an expression combining these parameters, and creates a small
1161+
function that returns the value of the expression::
1162+
1163+
lowercase = lambda x: x.lower()
1164+
1165+
print_assign = lambda name, value: name + '=' + str(value)
1166+
1167+
adder = lambda x, y: x+y
1168+
1169+
An alternative is to just use the ``def`` statement and define a function in the
1170+
usual way::
1171+
1172+
def lowercase(x):
1173+
return x.lower()
1174+
1175+
def print_assign(name, value):
1176+
return name + '=' + str(value)
1177+
1178+
def adder(x,y):
1179+
return x + y
1180+
1181+
Which alternative is preferable? That's a style question; my usual course is to
1182+
avoid using ``lambda``.
1183+
1184+
One reason for my preference is that ``lambda`` is quite limited in the
1185+
functions it can define. The result has to be computable as a single
1186+
expression, which means you can't have multiway ``if... elif... else``
1187+
comparisons or ``try... except`` statements. If you try to do too much in a
1188+
``lambda`` statement, you'll end up with an overly complicated expression that's
1189+
hard to read. Quick, what's the following code doing?
1190+
1191+
::
1192+
1193+
import functools
1194+
total = functools.reduce(lambda a, b: (0, a[1] + b[1]), items)[1]
1195+
1196+
You can figure it out, but it takes time to disentangle the expression to figure
1197+
out what's going on. Using a short nested ``def`` statements makes things a
1198+
little bit better::
1199+
1200+
import functools
1201+
def combine (a, b):
1202+
return 0, a[1] + b[1]
1203+
1204+
total = functools.reduce(combine, items)[1]
1205+
1206+
But it would be best of all if I had simply used a ``for`` loop::
1207+
1208+
total = 0
1209+
for a, b in items:
1210+
total += b
1211+
1212+
Or the :func:`sum` built-in and a generator expression::
1213+
1214+
total = sum(b for a,b in items)
1215+
1216+
Many uses of :func:`functools.reduce` are clearer when written as ``for`` loops.
1217+
1218+
Fredrik Lundh once suggested the following set of rules for refactoring uses of
1219+
``lambda``:
1220+
1221+
1) Write a lambda function.
1222+
2) Write a comment explaining what the heck that lambda does.
1223+
3) Study the comment for a while, and think of a name that captures the essence
1224+
of the comment.
1225+
4) Convert the lambda to a def statement, using that name.
1226+
5) Remove the comment.
1227+
1228+
I really like these rules, but you're free to disagree
1229+
about whether this lambda-free style is better.
1230+
1231+
12351232
Revision History and Acknowledgements
12361233
=====================================
12371234

0 commit comments

Comments
 (0)