Python is a little different from other languages like C/C++ and Java, in that functions are not magic constructs, but values. Yes, functions in Python, are like integers in C. They are simply values and can be passed to other functions/object constructors, and so forth. We've already seen a pretty simple example of a Python function in the form of the venerable "Hello World!" program. Let's take another look at it:
# hello.py
def hello():
print "Hello World!"
return
In the above code there is really only one new keyword: def. def is the keyword used when defining functions. Arguments are passed in paranthesis just like C, and the return statement can't return multiple values. However, since lists, tuples, and dictionariesNone.
Now let's play around around a bit with the idea of functions as objects, and then will show some some more advanced functions.
Let's write a simple function called "map", which maps a function across a list, applying that function to each list element. I'm also going to illustrate recursion by writing a recursive and non-recursive version of "map".
# map.py
# We can use append here
def map( fun, list ):
nlist = []
for item in list:
nlist.append( fun( item ) )
return nlist
# But here we have to use concatenation, or the + operator for lists.
def rmap ( fun, list ):
if list == []:
return []
else:
return [fun( list[0] )] + rmap( fun, list[1:] )
# Make a sample test function
def increment(x):
return x+1
# Test them out!
map( increment, [1,2,3,4,5] )
# should return [2,3,4,5,6]
map( increment, [1,2,3,4,5] ) == rmap( increment, [1,2,3,4,5] )
# There outputs should be the same!
Now notice how a function, in this case increment is passed to map and rmap, just as if it were a number or some other data. Why is that? Because functions are data in Python, not just special labels for code blocks(or whatever they are in C/C++). There is also a nice example of the difference between recursive and non-recursive code in Python. I find the first form more intuitive, but the second form is more interesting.
Pretty tame so far. Aside from the fact that functions are (cs321)first-class values(/cs321) in Python, they don't seem to exciting. So let's try showing off a few more features of Python functions.
Arguments in Python Functions
Multiple Return Values
Let's assume that I have a C++ code in which I want to return 3 values. There are a number of ways I could return
Possible Method I:
Possible Method II:
Now I am not saying that these are the only ways to return multiple values in C++, but they are a few possibilities. However, in Python, "Possible Method I" would not work. Python passes all arguments using "pass by reference". However, numerical values and Strings are all immutable in place. You cannot change the value of a passed in variable and see that value change in the caller. Dictionaries and Lists on the other hand are mutable, and changes made to them by a called function will be preserved when the function returns. This behavior is confusing and can lead to common mistakes where lists are accidentally modified when they shouldn't be. However, there are many reasons for this behavior, such as saving memory when dealing with large sets.
Most often when you have to return multiple arguments you will probably just use something simple like this example. Here we just take advantage of the fact that Python includes built in Tuples, Lists, and Dictionaries, and return one of these objects to encapsulate the multiple values.
# multiple-returns.py
a, b, c = 0, 0, 0
def getabc():
a = "Hello"
b = "World"
c = "!"
return a,b,c #defines a tuple on the fly
def gettuple():
a,b,c = 1,2,3 # Notice the similarities between this and getabc?
return (a,b,c)
def getlist():
a,b,c = (3,4),(4,5),(5,6)
return [a,b,c]
# These all work, as amazing as it seems.
# So multiple assignment is actually quite easy.
a,b,c = getabc()
d,e,f = gettuple()
g,h,i = getlist()
# It's fun too... Depending on how you design your code,
# chances are you'll never ever use it.
# But it's neat.
Sometimes, when dealing with lists and dictionaries it is actually more efficient or effective to take advantage of the fact that these more complex objects are passed-by-reference. Take a look at the following example:
Finally it is important to be aware of possible bugs that can occur with references to Lists and Dictionaries. Consider the following code intended to...( I need to dig up an example of one of these pass-by-reference gotchas.)
Python allows you to mess around with arguments a little more then C/C++. Let's see a few examples of how functions can be defined and how they can be used.
# Simplest Arguments
def multiprint( n, txt ):
i = 0
while i < n:
print txt
# This throws an error
multiprint()
# Default Values
def multiprint( n=5, txt="" ):
i = 0
while i < n:
print txt
# This works just fine
multiprint()
# Labels
def multiprint( n=5, txt="" ):
i = 0
while i < n:
print txt
# I want to call multiprint, but I'm happy with
# n = 5, so I don't want to reassign it.
# I can use the labels to set "txt" without having to set n.
multiprint( txt="Hello World!" )
# You can mix default and required variables
# Notice there is a default on list, but since it is
# defined in the middle of the list, you can't do this:
# fold_right( lambda x y: x + y, 0 )
# No, you need to specify lst as well, even though you
# may be happy with the default value.
# fold_right( lambda x y: x + y, [], 0 )
def fold_right( fun, list=[], base ):
if list == []:
return base
else:
return fun( list[0], fold_right( fun, list[1:], base ) )
# Now, let's say I want to just define fun and base, and not list.
# I could do this:
fold_right( fun = lambda x y: x + y, base = 0) #Look 'ma, no list!
# This makes sense if you have a large argument list (5, 10, + variables),
# some default and some required. You can easily specify just the
# variables necessary to run the function and leave the defaults alone.
Default arguments are nice, allowing you to simply define values to be used if none are specified. This makes long argument lists a lot nicer to work with. On the other hand, having default arguments, especially with complicated functions, you'll often end up with a mix of default and required variables. However as soon as your function requires a variable it requires you to either re-order your argument list, or specify a bunch of arguments with default values(whether you want to set those defaults or not). Python supplies you with an easy way to set just the arguments necessary and none that aren't. Simply specify the name of the variable in the call to the function. These "labels" can be used in any python function and they can help in dealing with complicated functions and arglists.
As if the last section wasn't cool enough, let's try "lambda expressions", a concept borrowed from Lisp and other functional languages. Lambda expressions return functions as results. They build functions without assigning names to them. A normal function declaration assigns the functions value to the name of the function, but lambda expressions do not.
Why would you want this dubious functionality? Because lambda expressions are nice to use in places that a function declaration wouldn't normally be allowed, and to write quick and dirty functions on the fly. Sometimes you want to build functions(callback handlers in gui's, object member data accessors, and so on), and using lambda expressions makes that a lot easier.
So lambda lets you define and use a function inside an if statement body, or inside a list. However, lambda expressions are just that, expressions. It is difficult to write a complicated function (unless you're a pro with languages like Lisp, Ocaml, etc.)
Let's do a few examples of where lambda expressions could be useful:
# lamba-map.py
# Remember our "map" function from a little earlier?
import map
list = xrange(0,2,100)
# Let's redefine increment as a lambda function.
increment = lambda x: x + 1
# This looks like how you would normally do it.
map.map( increment, list )
# Or, we're do lazy for that "=" statement above :~)
# That and increment is so simple...
map.map( lambda x: x + 1, list )
# Where lambda won't work:
# Lambda's cannot contain statements. So although the following
# code is almost valid Ocaml, it is _not_ valid Python. :~)
lmap = ( lambda f, lst:
if lst == []:
return []
else:
return [ f( lst[0] ) ] + lmap( f lst[1:] ) )
And that's all I have to say about functions for now.