functional programming

Functions and Functional Programming

Substantial programs are broken up into functions for better modularity and ease of maintenance. Python makes it easy to define functions but also incorporates a surprising number of features from functional programming languages.
Functions

Functions are defined with the def statement:

def add(x,y):
return x + y

The body of a function is simply a sequence of statements that execute when the function is called.You invoke a function by writing the function name followed by a tuple of function arguments, such as a = add(3,4).

You can attach default arguments to function parameters by assigning values in the function definition. For example:

def split(line,delimiter=’,’):
statements

The use of mutable objects as default values may lead to unintended behavior:

def foo(x, items=[]):
items.append(x)
return items

foo(1)
# returns [1]
foo(2)
# returns [1, 2]
foo(3)
# returns [1, 2, 3]

Notice how the default argument retains modifications made from previous invocations. To prevent this, it is better to use None and add a check as follows:

def foo(x, items=None):
if items is None:
items = []
items.append(x)
return items

A function can accept a variable number of parameters if an asterisk(*) is added to the last parameter name.

To pass a tuple args to a function as if they were parameters, the *args syntax can be used in a function call as follows:

def printf(fmt, *args):
# Call another function and pass along args
fprintf(sys.stdout, fmt, *args)

Function arguments can also be supplied by explicitly naming each parameter and specifying a value.These are known as keyword arguments. Here is an example:

def foo(w,x,y,z):
statements

# Keyword argument invocation
foo(x=3, y=22, w=’hello’, z=[1,2])

If the last argument of a function definition begins with ** , all the additional keyword arguments (those that don’t match any of the other parameter names) are placed in a dictionary and passed to the function.

You can combine extra keyword arguments with variable-length argument lists, as long as the ** parameter appears last:

# Accept variable number of positional or keyword arguments
def spam(*args, **kwargs):
# args is a tuple of positional args
# kwargs is dictionary of keyword args

Parameter Passing and Return Values

When a function is invoked, the function parameters are simply names that refer to the passed input objects.The underlying semantics of parameter passing doesn’t neatly fit into any single style, such as “pass by value” or “pass by reference,” that you might know about from other programming languages. For example, if you pass an immutable value, the argument effectively looks like it was passed by value. However, if a mutable object (such as a list or dictionary) is passed to a function where it’s then modified, those changes will be reflected in the original object.

The return statement returns a value from a function. If no value is specified or you omit the return statement, the None object is returned.To return multiple values, place them in a tuple:

def factor(a):
d = 2
while (d >> import foo
>>> def helloworld():
… return ‘Hello World’

>>> foo.callf(helloworld)
‘Hello World’

When a function is handled as data, it implicitly carries information related to the surrounding environment where the function was defined. As an example, consider this modified version foo.py that now contains a variable definition:

# foo.py
x = 42
def callf(func):
return func()

Now, observe the behavior of this example:

>>> import foo
>>> x = 37
>>> def helloworld():
… return “Hello World. x is %d” % x

>>> foo.callf(helloworld)
# Pass a function as an argument
‘Hello World. x is 37’

In this example, notice how the function helloworld() uses the value of x that’s defined in the same environment as where helloworld() was defined.Thus, even though there is also an x defined in foo.py and that’s where helloworld() is actually being called, that value of x is not the one that’s used when helloworld() executes.

When the statements that make up a function are packaged together with the environment in which they execute, the resulting object is known as a closure.
Decorators

A decorator is a function whose primary purpose is to wrap another function or class. The primary purpose of this wrapping is to transparently alter or enhance the behavior of the object being wrapped. Syntactically, decorators are denoted using the special @ symbol as follows:

@trace
def square(x):
return x*x

The preceding code is shorthand for the following:

def square(x):
return x*x
square = trace(square)

Generators and yield

If a function uses the yield keyword, it defines an object known as a generator. A generator is a function that produces a sequence of values for use in iteration. Here’s an example:

def countdown(n):
print(“Counting down from %d” % n)
while n > 0:
yield n
n -= 1
return

If you call this function, you will find that none of its code starts executing. For example:

>>> c = countdown(10)
>>>

Instead, a generator object is returned. The generator object, in turn, executes the function whenever next() is called (or __next__() in Python 3). Here’s an example:

>>> c.next() # Use c.__next__() in Python 3
Counting down from 10
10
>>> c.next()
9

You normally don’t call next() directly on a generator but use it with the for statement, sum(), or some other operation that consumes a sequence. For example:

for n in countdown(10):
statements
a = sum(countdown(10))

List Comprehensions

A common operation involving functions is that of applying a function to all of the items of a list, creating a new list with the results. For example:

nums = [1, 2, 3, 4, 5]
squares = []

for n in nums:
squares.append(n * n)

Because this type of operation is so common, it is has been turned into an operator known as a list comprehension. Here is a simple example:

nums = [1, 2, 3, 4, 5]
squares = [n * n for n in nums]

The general syntax for a list comprehension is as follows:

[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2

for itemN in iterableN if conditionN ]

To illustrate, here are some more examples:

a = [-3,5,2,-10,7,8]
b = ‘abc’

c = [2*s for s in a] # c = [-6,10,4,-20,14,16]
d = [s for s in a if s >= 0] # d = [5,2,7,8]
e = [(x,y) for x in a # e = [(5,’a’),(5,’b’),(5,’c’),
for y in b # (2,’a’),(2,’b’),(2,’c’),
if x > 0 ] # (7,’a’),(7,’b’),(7,’c’),
# (8,’a’),(8,’b’),(8,’c’)]

f = [(1,2), (3,4), (5,6)]
g = [math.sqrt(x*x+y*y) # f = [2.23606, 5.0, 7.81024]
for x,y in f]

Generator Expression

A generator expression is an object that carries out the same computation as a list comprehension, but which iteratively produces the result.The syntax is the same as for list comprehensions except that you use parentheses instead of square brackets.
The lambda Operator

Anonymous functions in the form of an expression can be created using the lambda statement:

lambda args : expression

args is a comma-separated list of arguments, and expression is an expression involving those arguments. Here’s an example:

a = lambda x,y : x+y
r = a(2,3) # r gets 5

The primary use of lambda is in specifying short callback functions. For example, if you wanted to sort a list of names with case-insensitivity, you might write this:

names.sort(key=lambda n: n.lower())

Recursion

Recursive functions are easily defined. For example:

def factorial(n):
if n >> factorial(6)
120
>>>
“””
if n <= 1: return 1
else: return n*factorial(n-1)

The documentation string is stored in the __doc__ attribute of the function that is commonly used by IDEs to provide interactive help.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s