Functional Tools in Python ---------------------------------------- A brief introduction to Functional Programming in Python. 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. What are Some Functional Languages? ---------------------------------------- Popular functional programming languages: - Scheme - Erlang - OCaml - Haskell - F# - Clojure - R 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. 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. 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. Function Nightmare (nightmare.py) ---------------------------------------- .. literalinclude:: /code/nightmare.py Getting Rid of Logical Blocks ---------------------------------------- Small logical blocks can often be removed by making use of Python short circuiting logical statements. .. code-block:: python x = 2 x_place = 'did not place' if x == 1: x_place = 'first' elif x == 2: x_place = 'second' print x_place Short Circuit Example ---------------------------------------- .. code-block:: python x = 2 print (x == 1 and 'first') or (x == 2 and 'second') or 'did not place' 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. .. code-block:: python def simple(x=None): x = x or {} # Do some more work 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. .. code-block:: python 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)] Double List Comprehensions (identity.py) ---------------------------------------- You can have nested list comprehensions to eliminate nested for loops. .. literalinclude:: /code/identity.py Anonymous Functions ---------------------------------------- So far we have always defined functions using the ``def`` keyword. You can also define anonymous (unnamed) functions with ``lambda``. .. code-block:: python f = lambda x: x*x # Create a new function and assign to f print f.__class__ print f(2) 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. .. code-block:: python mix = [1, 'a', 'b', 2, 4] letters = filter(lambda x: isinstance(x, basestring), mix) print letters 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``. .. code-block:: python # 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. Map ---------------------------------------- ``map`` applies a function to each item in an iterable and returns a list of the results. .. code-block:: python # 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]) Map Example (matrix.py) ---------------------------------------- .. literalinclude:: /code/matrix.py 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. .. code-block:: python 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 More List Sorting Examples (listsort.py) ---------------------------------------- .. literalinclude:: /code/listsort.py 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. .. code-block:: python print sorted((8, 6, 7, 5, 3, 0, 9)) print sorted('jenny') print sorted({'got': 'your number', 'need': 'make you mine'}) 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. Key Functions Examples (keys.py) ---------------------------------------- .. literalinclude:: /code/keys.py ``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 ``itertools`` Examples (iter.py) ---------------------------------------- .. literalinclude:: /code/iter.py 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. Closure Example (closure.py) ---------------------------------------- .. literalinclude:: /code/closure.py 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. Up Next ---------------------------------------- A tour of the Python standard library. .. header:: MA792K Spring 2011 Lecture 5 .. footer:: © Mark Lavin 2011