An Overview of Python: Tutorial 5 - Functions, pt 1
Author |
Message |
apython1992
|
Posted: Sat Jul 02, 2011 5:07 pm Post subject: An Overview of Python: Tutorial 5 - Functions, pt 1 |
|
|
It's been a while since my last tutorial, mainly because I've been busy with work among other things. Anyway, after some delay here is my fifth tutorial in my series, dealing with functions in Python. I've recently read over my previous tutorials, and after doing so I realized that I was juggling with trying to target different audiences; I wanted to introduce low-level programmers to some higher level concepts while keeping it elementary enough for new programmers to begin by reading these and learning the Python language. I've decided that it's hard to target both audiences (I think that by trying to balance between both I may have been lacking a certain level of depth in either direction), and so I'm going to keep going with these tutorials trying to target new programmers by focusing on general programming concepts with the implementation in Python, rather than simply showing more experienced programmers in other languages some of the great things about Python. I'll probably write some more tutorials on the side for this other purpose, but this series will target first-time programmers.
So, Functions!
What is a function in a programming language? In its purest sense, it's very similar to the functions you learn about in math class. In math, a function can be anything that relates two or more variables. Let's consider an example: the basic parabola. As many of us know, the equation of a parabola is y = x^2, or in a slightly more formal notation, f(x) = x^2. Looking at the latter notation, we can read this as follows: "A function acting on x will return x^2", or "For this function, if we input a value x, the output will be x^2". The basic parabola function takes one parameter as input, and acts upon the parameter to produce some output. For example, to find any point on this parabola, we choose an x value, and we can find the y value by substituting the number for x everywhere in the function. If we pick x = 3, then f(3) = 3^2 = 9, which means (3, 9) is a point on the parabola. Many functions in programming languages act in this way too - we expect the function to return a value based on the parameter(s) that were put into the function. In programming, functions provide a convenient way to reuse pieces of code when we want to achieve certain things over and over again. For example, let's say you are writing a physics engine in a game that relies heavily on solving quadratic equations to implement collision detection between various objects. How redundant would it be to have to re-write code to solve the equation over and over again for each slightly different object? Instead, you can write a generic function for solving quadratic equations based on parameters A, B, and C, and just call the function for unique sets of parameters. An easy first example would be - you guessed it - the parabola! Let's write a function in Python that returns the square of the input, just like the equation of a basic parabola.
Python: |
def square(x):
return x**2
|
Let's go through that. The def keyword in Python lets the interpreter know that we are defining a function. On that first line, we can read that as "I am defining a function called 'square' that takes one parameter, 'x', as its input". Great! Then we actually have to define the body of the function - what it is actually supposed to do. This one is easy - we want to get the square of the input. So, we use the return keyword to tell the interpreter that this statement should return this value to whatever entity called the function (we'll see this in action very shortly). And of course, here we want to return the square of the input, so after the return keyword, we say exactly that: x**2. The ** operator is used for exponentiation in Python.
Great. So we have defined this function, and if we were to save this in a file and run it, we wouldn't get any errors. But it also wouldn't do anything. Seriously, make a new Python source file with nothing but this function, and then run it. Nothing will happen. Why? Because here, all we've done is defined a function, and we have never actually used it. Let's extend this new program and actually make use of it. Let's start basic:
Python: |
def square(x):
return x**2
print square(5)
|
This time when you run it, you will see the number 25 appear in your console window. We actually made use of the function this time by calling it in our print statement. Similarly:
Python: |
def square(x):
return x**2
x = square(5)
print x
|
This does the same thing, and shows us something powerful about functions - we can store the result of a function into a variable! This function isn't very useful to us though, because we have the built-in exponent operator that we can always use. Let's look at a slightly more complicated example.
Python: |
def multiply(number_list):
result = 1
for num in number_list:
result *= num
return result
product = multiply(range(10))
print product
|
In this example, we can see that programming functions have some flexibility that we haven't yet seen in our mathematics courses: we can actually define a function to execute a set of statements and return a value, rather than simply returning a value in a single statement. The "multiply" function takes a list of numbers as its input, and returns the product of each number in the list as its output (the *= operator here is used as a shortcut result = result * num). The result starts with an initial value of 1, and then we cumulatively multiply it with each number in the list, and return that final value. Take a look at where we called the multiply function. Our input parameter to the multiply function was the return value of another function, range! We've seen the built-in range function before, but now we can say a little bit more about it: when one parameter (or argument) 'n' is supplied to the range function, the return value (output) is a list of numbers from 0 to n-1. This return value then gets used as the input parameter to multiply. So instead of having to create this list in one statement and find the product of its elements in another statement, we can nest the two function calls together to keep our code a little bit more concise. We shouldn't go too too crazy with nesting function calls though, because it starts to take away from code readability. I usually limit myself to three nested function calls in a line, and if I need more then I store the return values of the rest of the function calls in intermediate variables.
In imperative programming languages, functions can also simply define blocks of code to execute, without necessarily returning a value like we have just seen, or they may return a value but based only partially on the input parameters (i.e. functions that may depend on the state in a higher scope, like global variables). These functions are impure, because they aren't entirely deterministic. In the examples above, we knew exactly what to expect the function to return when we knew its input parameter, because that is all the function depended on. Functions like this are great to reuse because we can place a higher level of trust in them (no unexpected side effects), but they are limited because they cannot interact with the rest of a program or the user. Sometimes we need functions to be a little bit more transparent so they can interact with other parts of the program, even if this means they become less deterministic (we can't necessarily predict its execution based on only the input parameters). A very common example of this kind of function is any function that deals with an internet connection - downloading files from an FTP server, sending data through a socket, or downloading a podcast from a news website. All of these functions will have input parameters, but we can't be 100% sure how the functions will execute even if we know all of the input parameters: they depend on the state of the internet connection, so this limits us to saying that the function *might* download the podcast, but it also *might* break if there is no internet connection. Of course there are mechanisms for dealing with this, but the point is that we are no longer entirely sure how the function will execute. This applies to all functions that depend on some sort of program state. Let's look at a very basic example of a function that just does something without returning a value.
Python: |
def print_name_age(name, age):
print "{0} is {1} year(s) old."format(name, age)
print_name_age("Andrew", 19)
print_name_age("Ali", 34)
print_name_age("Ryan", 22)
|
The output of this program would be:
code: |
Andrew is 19 year(s) old.
Ali is 34 year(s) old.
Ryan is 22 year(s) old.
|
Here, we defined a function that would simply print a formatted version of the parameters to the console. It doesn't return a value, it just *does* something. "But wait - isn't this function completely deterministic because we always know what it will do?" Good question, but no. The print statement writes to stdout, but it isn't always directed to a console. It may be directed to a file or even another program, depending on how the program was executed. For example, in a GUI application the print statement would not write to a console. So this function does depend on the state of the program. Notice the difference here between using functions that return a value and those that don't. with functions that return a value, we need to specify what we are going to do with the return value - we store it into a variable, print it to the console, write it to the file, or something else to that effect. In fact, something always needs to be done with the return value of such functions, so there is even a default direction when we don't tell the interpreter what to do with it. Watch:
code: |
>>> def foo():
... return 4
...
>>> foo()
4
>>>
|
It automatically got directed to the standard output stream, because we didn't tell the interpreter what to do with our value. This contrasts with functions that don't return values - we just call them, and because they don't return values, we don't try to store their results into variables or anything. They just execute the statements in the body of the function.
If you looked at my foo function, you'd have noticed that it didn't take any parameters. Not all functions take parameters, and this mostly happens with those functions that don't return values. When functions don't take any parameters, they should naturally be called with just an empty pair of brackets. You'd use functions like this when you just want to reuse repetitive code that never changes and thus doesn't require any parameters (like printing out the main menu of a text adventure game).
I think I'll stop here for this first part on functions, and leave you with a couple exercises to work on.
1. Write a function that takes two numbers as arguments and returns their product.
2. A factorial of a number n, written n!, is equal to n * (n - 1) * (n - 2) ... * 1. For example, 4! = 4 * 3 * 2 * 1 = 24. 0! is defined to equal 1. Write a function that takes a positive number (including zero) as an argument and returns the factorial of the number. Test your function with various inputs.
That's it for now! My second part on functions will look at some more real-life examples of functions working together in larger applications, optional parameters, and higher-order functions. We'll do a full hands-on example to see everything we've learned so far put together in a single program. Stay tuned! |
|
|
|
|
|
Sponsor Sponsor
|
|
|
SNIPERDUDE
|
Posted: Sat Jul 02, 2011 9:59 pm Post subject: RE:An Overview of Python: Tutorial 5 - Functions, pt 1 |
|
|
Thanks for continuing your Python tutorials. This forum really does need more Python |
|
|
|
|
|
|
|