Up: Python

Functions

slide 001 Hello, and welcome to the eighth episode of the Software Carpentry lecture on Python. In this episode, we’ll show you how functions work, and how to define new functions of your own.
slide 002 First, a bit of design philosophy. A programming language should not try to include everything that anyone might ever want, because (a) it’s impossible, and (b) the resulting language would be so large that it would be impossible to learn.
slide 003 Instead, languages should make it easy for people to create what they need to solve their specific problems.
slide 004 Every language does this by allowing programmers to define functions that carry out new higher-level operations.
slide 005 Which leads some people to regard programming as the act of creating a mini-language in which the solution to the original problem is trivial.
slide 006 In Python, we define new functions using the keyword def.
slide 007 For example, here’s a function that does nothing but return a particular string to its caller.
slide 008 To call it, we just use the function’s name, followed in this case by empty parentheses. If we print the result, it’s the string the function returned.
slide 009 To make functions more useful, we can give them parameters.
slide 010 Here’s another greeting function that takes the name of the person being greeted as a parameter. What it returns depends on that parameter’s value.
slide 011 Let’s have a closer look at what happens when we call it. First, we assign the string 'doctor' to the variable temp.
slide 012 When we call greet with temp as a parameter, Python creates a variable called name and copies the reference in temp into that variable. temp and name are now aliases for the same string.
slide 013 Inside the function, another new variable called answer is created to hold the result of concatenating 'Hello, ' and the value of name.
slide 014 When the function returns, its variables name and answer are discarded, but the value that answer was pointing at is returned and assigned to result.
slide 015 As this example shows, each function call creates a new set of variables called a stack frame. Each function call’s frame is stacked on top of those that are currently active, just like the plates in my sink.
slide 016 Let’s have a look at another example. Here we have two functions: add and double.
slide 017 In our main program, we give val the value 10.
slide 018 We then call the function double, which creates a new stack frame. Initially, that stack frame only contains the variable c, which is the function’s sole parameter, and that variable points at the value that was passed into the function.
slide 019 Since double immediately calls add, Python immediately pushes another frame on top of the stack. Since add also only has one parameter, this frame initially only contains the variable a. Our 10 is now pointed at by three aliases, one in each of three different frames.
slide 020 Now we finally do some computation: b is assigned the value of a plus 1. Python creates the new value in memory, then creates a new variable b in the current stack frame to point to it.
slide 021 Next, the function add returns that value to its caller. add‘s stack frame is popped off the stack and thrown away; the 11 it returned is multiplied by 2 and the result assigned to a new variable d. This variable is in double‘s stack frame, as shown here.
slide 022 Now it’s double‘s turn to return. Its result, 22, is assigned to result, and double‘s stack frame is discarded.
slide 023 The program can finally print the result of this sequence of calls. Every modern programming language uses some variation on this basic model: each function call creates a new stack frame, which has its own set of variables, and which is discarded when the function returns.
slide 024 Stack frames also determine what variables are visible, or in scope, at any time. In Python, the program can only “see” variables in the current frame and the base “global” frame (which isn’t part of any function call).
slide 025 If the current and global frames have variables with the same name, the one in the current frame takes precedence.
slide 026 Here’s an example: we define a function that, when called, will create a local variable temp, and then create a global variable that’s also called temp before calling the function.
slide 027 This is what’s in memory just before the function returns. As you can see, the function’s temp is a separate variable from the global temp, and when the function refers to temp, it means its temp, not the global one.
slide 028 The final output of this contrived little program is therefore what’s shown here.
slide 029 In our examples so far, we have always assigned the values returned by functions to variables before using them so that our diagrams will be easier to draw. We don’t have to do this: Python will create hidden temporary variables for us when and as needed.
slide 030 For example, here’s our greeting program rewritten so that the only explicit variable is the parameter name to the function greet.
slide 031 Just before the function returns, Python has created two such variables: one in the global frame to refer to the string 'doctor' before it’s passed into the function, and one inside the function’s stack frame to temporarily store a reference to the result that is to be returned. Their actual names are a lot longer, and less readable, than _x1_ and _x2_, but that doesn’t matter: they’re never visible to programmers.
slide 032 Python has an explicit return keyword because a function can return a value at any time.
slide 033 Here, for example, is a function that returns the sign of a number.
slide 034 If we call it with the parameter 3, the first branch of the if executes and returns 1.
slide 035 If we call it with -9, the return in the else is executed.
slide 036 Returning as soon as a value is known is handy, but over-use can make functions hard to understand, since people have to read the whole thing to figure out how it might behave.
slide 037 There are no hard and fast rules for what’s good practice, and what’s abuse, but most programmers would agree that…
slide 038 it’s OK to have a small number of “early returns” at the very start of the function to handle special cases, and then…
slide 039 …one at the end to return the “general” result. We’ll see examples of this style later on when we start to write larger programs.
slide 040 One important thing to note about Python is that every function returns something, even if it doesn’t have an explicit return statement.
slide 041 To see how this works, let’s comment out the last two lines of our sign function.
slide 042 The sign of 3 is still 1…
slide 043 …but now, the sign of -9 is None.
slide 044 The rule in Python is that if a function doesn’t explicitly return something else, it returns None. Other languages do this differently: in C, for example, trying to assign the “result” of a function that doesn’t return one is a compilation error—the program can’t even be run.
slide 045 This kind of behavior is one more reason why commenting out blocks of code is a bad idea: it’s all too easy to accidentally get rid of a return statement, after which your function will silently be telling its caller “no data”.
slide 046 Another important feature of functions in Python is that, like variables, they don’t have specific types: their parameters can be anything at all, and so can their return values.
slide 047 For example, here’s a function called double that multiplies its argument by 2.
slide 048 double of 2 is 4…
slide 049 …and double of the string 'two' is the string 'twotwo'. In the first case, the function took an integer and returned an integer; in the second, it took a string and returned a string. Python’s quite happy to do this…
slide 050 …since there’s nothing in the function that depends on its parameter having either specific type. For obvious reasons, you should only rely on this behavior when the function only does things that will work on all possible types of input.
slide 051 It’s possible to write code like this, that does different things depending on parameters’ actual types…
slide 052 …but it’s almost always a sign of bad design, since it will have to be rewritten every time you want to generalize the function.
slide 053 If you really want to do this, there’s a much better way, which we will explore in detail in the lecture on object-oriented programming.
slide 054 For now, let’s go back to functions. As we said earlier, references to values are copied into parameters when the function is called.
slide 055 This creates aliases: in particular, it means that when a list is passed into a function, what’s actually passed in is an alias for the list.
slide 056 To explore what this means in practice, here’s a function that takes a string and a list as parameters, and appends something to both.
slide 057 Here’s some code to set up a pair of variables and call that function.
slide 058 Just before the call, the global frame has two variables, as shown here.
slide 059 The call places a new frame with aliases for those variables on top of the stack.
slide 060 The statement a_string += 'turing' creates a new string and overwrites the value of a_string with a reference to it.
slide 061 The statement a_list.append('turing'), however, actually modifies the list that a_list is pointing at, which is the same thing that list_val in the caller is pointing it.
slide 062 Sure enough, when the function returns and the call frame is thrown away, the new string 'alanturing' is lost, because the only reference to it was in the stack frame. The change to the list, on the other hand, is kept, because the function actually modified the list in place. We’ll explore this idea of passing references around in more detail in a later lecture: it turns out to be fundamental to a lot of computational thinking.
slide 063 First, though, let’s finish our look at Python functions. To avoid writing redundant code, we can define default values for parameters.
slide 064 Here, for example, we’ve defined an adjust function that has two parameters, but knows to use the value 2.0 for the second one if the caller doesn’t pass a value in.
slide 065 If we call this function with one parameter, it is assigned to value, and 2.0 is used for amount.
slide 066 If we call it with two parameters, the second overrides the default for amount.
slide 067 One function with default values is usually easier to read than several functions, each taking a different number of parameters.
slide 068 For example, if Python didn’t support default values, we would probably write two functions: one to handle the general case, and another with a slightly different name to handle the common case that did nothing but call its more general cousin.
slide 069 One restriction, though, is that all of the parameters that have default values must come after all of the parameters that don’t.
slide 070 To see why, imagine we were allowed to mix defaulting and non-defaulting parameters as shown here.
slide 071 If we call the function with just one parameter, it’s pretty clear that its value has to be assigned to middle
slide 072 …but what should the program do if the function is called with two parameters?
slide 073 Should it use the provided values for the first and second parameters, and the default for the third…
slide 074 …or use the first parameter’s default, and assign the given values to the second and third? We could define a rule, but it’s simpler and safer to disallow the problem in the first place.
slide 075 To close off this episode, let’s try to answer a frequently-asked question: when should we write functions? And what should we put in them?
slide 076 The answer depends on the fact that human short-term memory can only hold about seven items at a time. If we try to remember more unrelated bits of information than that for more than a few seconds, they become jumbled and we start making mistakes.
slide 077 In particular, if someone has to keep a dozen things straight in their mind in order to understand a piece of code, that code is too long.
slide 078 Functions are a way to divide code up into more comprehensible pieces: essentially, to replace several pieces of information with one to make the whole easier to understand.
slide 079 Functions are therefore not just about eliminating redundancy: they’re worth writing even if they’re only called once.
slide 080 Here’s an example. This code uses meaningful variable names, and is well structured, but it’s still too much to digest in one go.
slide 081 Let’s start by replacing the loop bounds with function calls that give us a bit more context. grid_interior of arg might just return range(1, arg-1), but try reading the first two lines of this code aloud, and then the first two lines of what it replaced. This version is easier to understand.
slide 082 Now let’s replace those two conditionals with function calls as well. Again, we’ve reduced the number of things our eyes have to scan, and provided more information about what we’re actually doing.
slide 083 Finally, let’s call a function that handles updates to our data structure. Our original nine lines have become five, and those five are all at the same level. It’s hard to pin down exactly what that means, but most programmers would agree that the first version mixed high-level ideas about boundaries and update conditions with low-level details of grid access and cell value comparisons. In contrast, this version only has the high-level stuff; the low-level implementation details are all hidden in those functions.
slide 084 A good programmer would probably actually write the code shown here right off the bat…
slide 085 …then go back and write the functions that it assumes…
slide 086 …and finally refactor. Essentially, the best way to program is to pretend that the special-purpose computer you wish you had already existed, then bring it into existence piece by piece.
slide 087 We’ll see more of this top-down style of program design in future lectures.

  1. No comments yet.
  1. No trackbacks yet.