Functional Programming
Python always supported the functional programming style with the lambda, map, filter, and reduce constructs. Python 2.5 adds partial function application and the built-in functions
any() and
all(). Guido Van Rossum (inventor of Python) actually thinks that the original lambda, map, filter, and reduce functions should be dropped in favor of nested functions (instead of lambda) and list comprehensions (instead of map and filter). He is not too happy about reduce either, which is why he pushed for the inclusion of
any() and
all(). Check out this blog for his reasoning:
http://www.artima.com/weblogs/viewpost.jsp?thread=98196.
The following sample program demonstrates all(), any(), and partial() at work. The all() function takes an iterable and returns True if all the elements are True, otherwise False. The any() function takes an iterable too and returns True if any of the elements are True, otherwise False. It doesn't get much simpler, but it's very useful. You could write them yourself of course, but these function are written in C for maximal performance.
In the code I defined two functionsall_divided_by() and any_divided_by()that use all() and any() respectively. all_divided_by() takes a sequence of numbers and a divider x and invokes all(0 on the generator expression (e % x == 0 for e in seq), which will be True for any number that can be divided by x with no remainder. I'll leave it to you to figure out what some_divided_by is doing.
def all_divided_by(seq, x):
return all(e % x == 0 for e in seq)
def some_divided_by(seq, x):
return any(e % x == 0 for e in seq)
if __name__=='__main__':
s = [2, 4, 6]
x = 2
print 'All elements of', s, 'can be divided by', x, ':', all_divided_by(s, x)
x = 3
print 'All elements of', s, 'can be divided by', x, ':', all_divided_by(s, x)
print 'Some elements of', s, 'can be divided by', x, ':', some_divided_by(s, x)
x = 7
print 'Some elements of', s, 'can be divided by', x, ':', some_divided_by(s, x)
I tested these functions with the sequence 2, 4, 6 that all its elements are divisable by 2 but only some of them (6) are divisible by 3. I tried both functions with the dividers 2, 3, and 7. Here is the result:
All elements of [2, 4, 6] can be divided by 2 : True
All elements of [2, 4, 6] can be divided by 3 : False
Some elements of [2, 4, 6] can be divided by 3 : True
Some elements of [2, 4, 6] can be divided by 7 : False
Partial is a higher-level concept. It allows partial application of functions. This is similar to partial template specialization in C++ except that it's done at runtime and not at compile time. The idea is that if you have a function that accepts some arguments you can feed it only some of these arguments and what you will get is a new function that accepts only the unsupplied arguments. When you invoke it, it uses the original arguments and calls the original function with the union of all the arguments. It sounds more complicated than it should be, so let's see some code. I used partial to specialize the
all_divided_by(). I passed x=2 and I got a new partial function that I called
evens(). This function accepts just a sequence (where the original
all_divided_by() accepted a sequence and a divider) and returns True if all the numbers are even. It does it of course by invoking all_divided_by using the supplied x=2 argument.
import functools
all_evens = functools.partial(all_divided_by, x=2)
print 'All elements of', s, 'are even numbers:', all_evens(s)
s.append(7)
print 'All elements of', s, 'are even numbers:', all_evens(s)
The output is very conclusive.
all_evens() behaves like a function that detects even numbers.
All elements of [2, 4, 6] are even numbers: True
All elements of [2, 4, 6, 7] are even numbers: False
all_evens() looks like a function, but it is not a full-fledged function. It doesn't have the
__name__ and
doc__ properties. Here is some code that demonstrates that
all_divided_by has a
__name__, but
all_evens doesn't:
print '%s: %s' % ('all_divided_by.__name__', all_divided_by.__name__)
try:
print '%s: %s' % ('all_evens.__name__', all_evens.__name__)
except AttributeError:
print 'all_events have no __name__ attribute'
Trying to access
all_evens.__name__ raises an AttributeError that I catch and print. Here is the output.
all_divided_by.__name__: all_divided_by
all_events have no __name__ attribute
Partial can be used in diverse scenarios such as callback functions, hidden cookies, and API adaption. Partial function application is a generalization of currying, which is the hallmark of languages such as Haskell.