← Back to the Introduction to Computer Science outline The Course Maker
← Introduction to Computer Science outline
Week 7 · Lecture

Lecture: Functions

Introduction to Computer Science Free sample edition

Lecture script · about 15–20 minutes spoken · Week 7

Opening

Think about a dosage calculator that a nursing student uses on every chart on the ward. Nobody retypes the conversion rule for each chart. The rule is written once, and each chart hands it a different weight or dose.

A business major's spreadsheet applies one tax rule to every line of a receipt. A phone game tallies each round with the same few lines of code. In each case, the work is packaged once, handed new values, and asked for an answer it hands back.

In Week 2 you computed pages left in the reading log with one subtraction. In Weeks 5 and 6 you learned to repeat work with while and for loops. This week you learn to give a piece of work a name, so you can run it whenever you want and with whatever values you need. Next week is the midterm review, and this week's ideas are the last new ones it covers.

Here's the plan for this lecture. We'll define a function, hand it values, get a value back, and tell return from print. Then we'll see where a function's names live, fix the three classic function bugs, and check a chatbot's claim with a run.

1. Define once, call by name

Picture a bus driver who learns one route. Every morning the dispatcher says "run Route 9," and the driver follows the same streets. Learning the route happened once, long before.

Python's version of a learned route is a function. A function is a named block of code that does one piece of work; it runs only when a line of the program calls it by name, and it can be called as many times as you need. You write one with def. It's the keyword that starts a function definition, written as def, then the function's name, then its parameters in parentheses and a colon, with the function's body indented below. And the line that runs it is a function call: the function's name followed by parentheses holding the values to hand in. The body runs once for each call.

Here's the first example. The program defines a function named greet. Its body is one indented line that prints the words "Hello from inside the function." Below the definition, with no indentation, a line prints "Defined, not yet called." Then come two calls to greet.

Python reads the def block and remembers the name greet and its body, but it prints nothing yet. The first line it actually runs prints "Defined, not yet called." Then the first call jumps into the body and prints the greeting. The second call prints it again. One definition, two calls, two greetings.

The classic mix-up is believing that defining a function runs it. But a def block only stores the body under a name.

Pause and try this. In the greet program, which line of output comes first, and how many times does the greeting appear?

Find the first line that isn't inside the def block.

Here's the answer. "Defined, not yet called" comes first, and the greeting appears twice. The def block printed nothing, and each of the two calls ran the body once.

So def stores the recipe, and the call cooks it.

2. Hand values in with parameters and arguments

Think of a vending machine with two slots, one for the item code and one for the coins. The slots are fixed parts of the machine. What you drop into them changes every time.

A function's slots are its parameters. A parameter is a name written inside the parentheses of a def line; when the function is called, it receives the value supplied in the matching position of the call. The values you write in the call are its arguments, the same word Week 1 used for the values handed to print. Parameters live in the def line. Arguments live in the call.

Here's the reading log's stage for this week. The function is named pages_left, and its def line names two parameters, goal and pages_read. Its body is one line: return goal minus pages_read. The program then prints three calls.

The first call is pages_left with 300 and 45. Python matches by position: the first argument goes to the first parameter, so goal gets 300, and pages_read gets 45. The body computes 300 minus 45, which is 255, and the program displays 255. The second call hands in 300 twice. 300 minus 300 is 0, so it displays 0, and the goal is met.

Now swap the arguments, 45 first and 300 second. Goal gets 45 and pages_read gets 300. The body computes 45 minus 300, and the program displays negative 255. Python didn't complain. It did exactly what you wrote, not what you meant.

Pause and try this. In pages_left with 45 and 300, which parameter receives 300, and why?

Line up each argument with the parameter in the same position.

Here's the answer. Pages_read receives 300, because it's the second argument and pages_read is the second parameter. Python matches arguments to parameters by position only.

Parameters are the slots, and arguments fill them in order.

3. Send a value back with return

Think of a calculator. It does the arithmetic and holds the result, and you might write it down, read it aloud, or add to it.

A function hands back a result the same way, with return. Return is the keyword that ends a function's call and hands the value written after it back to the line that made the call, where that value can be stored, printed, or used in an expression. The value that comes back has a name too. A return value is the value a function call hands back to the code that called it; it is the value of the call expression itself.

So a call with a return value acts like the value. There are three ways to use it. Store it: the line left equals pages_left with 300 and 45 puts 255 into the name left. Print it: print left displays 255. Use it in an expression: left floor-divided by 30 is the same floor division as Week 2, and it gives 8, which is 8 full days of reading at 30 pages a day.

The reading log's third line uses the call directly inside an expression. Pages_left with 200 and 45 returns 155, because 200 minus 45 is 155. Then 155 floor-divided by 30 is 5. Five times 30 is 150, which fits inside 155, and 6 times 30 is 180, which doesn't.

Two more settings show the same move. A temperature converter takes degrees Celsius and returns the Fahrenheit value. For 20 degrees, 20 times 9 is 180, 180 divided by 5 is 36, and 36 plus 32 is 68. The program displays 68.0, with the point zero, because true division always gives a float. And a receipt function multiplies a price by a quantity. A price of 4 and a quantity of 3 returns 12. Add a second call, a price of 2 and a quantity of 5, which returns 10, and the sum is 22.

Pause and try this. The receipt program adds two calls in one line. Which happens first, the plus sign or the two calls?

Think about what plus needs before it can add.

Here's the answer. The two calls happen first. Each returns its own value, 12 and then 10, and only then does plus add them to get 22.

A call with return acts like the value it hands back.

4. Tell return from print

Picture a friend who whispers a phone number to you, and another friend who writes it on a card and hands it over. You heard the whispered number once. The card is something you can keep and use later. Print whispers. Return hands over the card.

When a function has no return line, it still hands something back. It hands back None, which is Python's special value meaning no value at all. A call whose function reaches the end of its body without a return line hands back None.

Here's the pinned example. A function named show takes one parameter, x. Its body is one line that prints x times 2. Then the program prints the result of calling show with 4.

Follow it in order. The outer print needs a value, so Python runs the call first. Inside the call, x is 4, and the body prints 8. That's the first line of output. The body ends with no return line, so the call hands back None. The outer print now displays what it was handed, so the second line of output is the word None.

A café tip calculator makes the same choice. If its body prints the bill plus 3, then storing the call in a name called total puts None into total, even though 23 appeared on the output. Change the word print to return, and total holds 23.

The classic mix-up is to think print and return do the same thing. In a small program both seem to put a number on the output. But print sends text to the output and hands nothing back, while return hands a value to the code and displays nothing.

Pause and try this. How many lines does print of show of 4 display, and what does the last one say?

Remember that the outer print needs a value from the call.

Here's the answer. It displays two lines, and the last one says None. The 8 came from the print inside the function, and None came from the outer print, because show has no return line.

No return means None.

5. Read scope: local names, global names, and the frame

Think of a hotel room safe. You can put things into it during your stay, and when you check out, it's emptied for the next guest. From your room you can still read the notices posted in the lobby, and nothing you write on your own notepad changes them.

A function works like that room. A local variable is a name created inside a function, including each parameter; it exists only while that call runs and is gone once the call ends. A global variable is a name created at the top level of a program, outside every function; code inside a function can read it, but a plain assignment inside the function makes a separate local name instead of changing it. Scope is the part of a program in which a name can be used. And a frame is the box of local names that Python sets up when a function is called and removes when that call returns.

Here's the scope program. At the top, level gets 100. A function named show prints the words "inside sees" and then level. A second function, reset, assigns level equals 0 and prints "inside" and level. The program calls show, calls reset, and then prints "outside" and level.

Show has no level of its own, so it reads the global and displays "inside sees 100." Reset's assignment makes a new local named level in reset's frame, so it displays "inside 0." When reset returns, its frame vanishes, along with its local level. The last line reads the global, untouched, and displays "outside 100."

The pinned local program makes the same point in four lines. N gets 5 at the top. A function named change assigns n equals 99 inside. After the call, the program prints n, and it displays 5.

Python Tutor draws this as a picture you can step through: a frame appears beside the global names when a call starts, and it disappears when the call returns.

Pause and try this. In the program where change sets n to 99, why does the last line display 5?

Ask which frame the 99 was written into.

Here's the answer. The 99 went into a local n in change's own frame, and that frame vanished when the call ended. The global n was never touched, so it's still 5.

A call gets a fresh frame, and the frame leaves when the call does.

6. Find and fix the three function bugs

Think of a mechanic who checks the three most common reasons a car won't start before anything else. Most function bugs are one of three slips, and each has a sign you can recognize.

Bug one is a forgotten return. Pages_left computes goal minus pages_read into a local named left, but it never returns it. Printing the call displays None where 255 should be. The fix is one line at the end of the body: return left.

Bug two is printing instead of returning. A function named double prints x times 2. The program stores double of 4 in y and then prints y plus 1. The run displays 8, and then Python raises a TypeError, because y holds None, and None plus 1 meets the wrong type. Read the error from the bottom up, and its last line names TypeError. The fix is to return x times 2. Then y holds 8, and the program displays 9.

Bug three is a local used outside. The program calls pages_left, which returns left, but it doesn't store the result. Then it tries to print left at the top level. Python raises a NameError, because left lived only in the call's frame. The fix is to store the returned value in a name of your own: left equals pages_left with 300 and 45, and then printing left displays 255.

In all three bugs the arithmetic was right; the problem was how the value left the function.

Pause and try this. A program displays 8 and then stops with a TypeError when it adds 1. Which of the three bugs is it?

Think about which bug displays a value before it fails.

Here's the answer. It's the function that printed instead of returned. The 8 came from the print inside the function, and the TypeError came from adding 1 to the None it handed back.

None, TypeError, NameError: check how the value leaves the function.

7. Check a chatbot's claim by running it

Think of a friend who tells you the shop on the corner closes at nine. You can trust it, or you can read the sign on the door. A chatbot's explanation of code is the friend's claim, and the run is the sign on the door.

Suppose you ask a chatbot what print of show of 4 displays. It answers that show returns 8, so the program displays 8. You paste the pinned program into your browser runner and run it. The output is two lines: 8, then None. The chatbot treated a printing function as if it returned the printed value, which is exactly the slip from Section 4. If you want its claim to be true, change show's print to return, and the run displays 8 alone.

A chatbot predicts likely text; it doesn't run your program. For the official word, the Python Tutorial's section 4.8 on docs.python.org is where defining functions is written down. One honest limit of this lecture: default values for parameters, arguments passed by name, the global keyword, and functions that call themselves all come later.

Pause and try this. A chatbot's prediction for print of show of 4 matches the first line of the run. Is that enough to trust it?

Compare every line of output, not only the first.

Here's the answer. No. The run displays a second line, None, that the chatbot left out. A claim is confirmed only when every line it predicts matches the run.

The chatbot proposes, and the run decides.

Closing

Let me pull it together in the order we built it. A function is defined once with def and runs only when called, so the greet program printed its greeting twice, after the line that came first. Arguments bind to parameters by position, so pages_left with 300 and 45 gives 255, and swapping them gives negative 255. Return hands a value back, so you can store it, print it, or use it, and pages_left with 200 and 45, floor-divided by 30, gives 5. Print is not return, so a printing function hands back None. A local lives in its call's frame and vanishes when the call ends, so the global level stayed 100. The three bugs show up as None, a TypeError, and a NameError. And every claim about a function gets checked by a run.

Next in this module come the slide deck, the Readings and Resources page, the lecture tutorial, the practice set, the quiz, and the Coding Lab. Then, in Week 8, the Midterm Review and Exam, you'll bring Weeks 1 to 7 together and read programs for the classic slips, including the one you met this week: no return means None.


Built with The Course Maker · samples.thecoursemaker.com