List Comprehension, Functions as Objects, Testing, and Debugging
MIT 6.100L Intro to CS and Programming using Python, lecture 12, explained in plain English
Want this lecture to stick? In LessonBound you get checked practice questions for every part, can ask about anything you don't get, and can listen with your screen off. Free, no ads.
Learn this lecture freeWatch or read the original lecture: MIT OpenCourseWare.
Part 1 · 0:00–10:00
This part shows how Python can build a new list from an old one in a single line, called a list comprehension.
Key ideas
- A common pattern is to make a new empty list, loop through an input list, apply the same operation to each element, and add the result to the new list. The lecturer calls this a very common thing programmers do. 1:20
- A list comprehension packs that whole loop into one line inside square brackets. The form is: the operation, then 'for each e in the list'. 2:26
- You can add an optional 'if' test at the end. Only the elements that pass the test get the operation applied. 4:54
- The loop can run over anything that gives a sequence of values, such as a range, a tuple, or a list. The lecturer says the main benefit is that the code is easier to read. It is not necessarily faster. 5:40
Words to know
- list comprehension
- A short one-line way to write a loop that builds a new list, with the square brackets holding the rule for each item.
- iterable
- Anything Python can step through one item at a time, like a list, a tuple, or a range.
- range
- A way to make a series of whole numbers. For example, range(6) gives 0, 1, 2, 3, 4, 5. It stops before the end number.
- expression
- A piece of code that works out a value, such as e squared.
Example
Picture a cookie line where a worker takes every cookie off a tray, puts frosting on each one, and only sends the ones that pass a check onto a new tray. The list comprehension works the same way. It takes each item, applies one step to it, and keeps only the items that pass the test.
3 checked practice questions for this part are in the app.
Part 2 · 10:00–20:00
This part shows how list comprehensions build lists in one line, and how default parameters let a function have a standard setting that callers can change.
Key ideas
- A list comprehension builds a new list in one line. The for part says which values to go through, and an if part can keep only some of them. 10:19
- To read a list comprehension, go step by step. First find the values being looped over, then check which ones pass the condition, then see what function is applied to those. 11:50
- A default parameter is a function input that has a standard value already set. A caller can skip it to use that value, or give a different value. 15:52
- The bisection_root function returns its approximation of a square root. Its precision, called epsilon, is fixed at 0.01 in the current code. 18:46
- Fixing epsilon inside the function would give every caller the same precision. It is better to let each caller choose how precise they want the answer. 19:29
Words to know
- list comprehension
- A short way to write a loop that builds a new list in one line, inside square brackets.
- condition
- A test written after the loop, such as 'if this is true', that decides which items are kept.
- default parameter
- An input to a function that already has a standard value, so the caller does not have to give one.
- epsilon
- The small amount of closeness we want, here how near the answer must be before we stop guessing.
Example
Think of a coffee shop order form where the size is already set to medium. If you do not say a size, you get medium, but you can ask for small or large when you want something different. A default parameter works the same way: it has a standard value that a caller can skip or change.
3 checked practice questions for this part are in the app.
Part 3 · 20:00–30:00
This part explains why a function should take its settings as parameters, how default parameters let the author pick a fallback value, the rules for calling functions with them, and how a function is stored as an object in memory.
Key ideas
- Using a global variable for epsilon is risky, because anyone using the code might forget to set or reset it. The lecturer wants the function to control its own epsilon. 20:16
- Making epsilon a second parameter means the caller chooses it each time. This works, but it is wordy, and users may not know a good value. 21:00
- A default parameter is a parameter with a value written into the function definition. If the caller leaves it out, Python uses that default value. 23:35
- Parameters with defaults must come after parameters without defaults. Callers can pass values by position, by name, or in a mix, but position matching can still give wrong results if you are careless. 25:22
- Writing a function definition creates a function object in memory. The code inside does not run until the function is called, and a second name can point to the same function object. 29:16
Words to know
- global variable
- A variable that lives outside any function, so any code in the program can read or change it.
- parameter
- A named input that a function expects when it is called, like x or epsilon.
- default parameter
- A parameter that already has a value set in the function definition, so the caller can skip it.
- alias
- A second name that points to the same object in memory, so both names lead to the same thing.
Example
Think of a coffee shop where the barista makes a medium latte unless you say otherwise. The medium size is the default, and you only need to name a different size if you want one. A default parameter works the same way: the function uses its standard value unless the caller gives a different one.
3 checked practice questions for this part are in the app.
Part 4 · 30:00–40:00
This part shows that a function can hand back another function as its result, and how Python finds the right variables when that returned function runs.
Key ideas
- A function name is just a label for a function object stored in memory. Writing my_func = is_even gives a second label to the same object. Nothing runs until parentheses are added. 30:21
- A function can return a function object instead of a call's result. Writing the name with no parentheses hands back the function itself. 31:25
- A function defined inside another function exists only while that outer function runs. The main program can use it only if the outer function returns it. 36:00
- Writing make_prod(2)(3) chains two calls. The first call returns a function, and the second call runs that returned function with 3. 33:07
- When the returned function runs, Python first looks for its variables in its own scope. If it cannot find a variable there, it checks the scope of the function that created it. That is how a is found as 2, and the result 6 comes back. 39:21
Words to know
- function object
- A function stored as a thing in memory that a name can point to. Giving it another name or passing it around does not run it.
- environment
- The private workspace Python makes for each function call, holding that call's variables. Each new call gets a new one.
- scope
- The area of a program where a variable name can be found and used. A variable inside a function is usually only visible inside that function.
- formal parameter
- A variable name in a function's definition that waits for a value. When the function is called, the value passed in is matched to this name.
Example
Think of a workshop that builds a custom tool with a fixed setting already inside it, then hands the finished tool out the door. The tool still works with the setting it was built with, even though the workshop is closed to the customer. Calling make_prod(2) is like ordering the tool, and calling the result with 3 is like using it.
3 checked practice questions for this part are in the app.
Part 5 · 40:00–50:00
This part shows how a function can hand back another function, why that helps organize and control code, and why testing and debugging are worth learning.
Key ideas
- A function can return a function object, which is a piece of code that can be called later. Because make_prod returns a function, you can chain calls like make_prod(2)(3). If it returned a plain number, the second call would fail. 40:17
- You can save the returned function in a variable, such as doubler = make_prod(2), and then call it with doubler(3). This gives the same result as chaining, but it uses more lines. 41:00
- Two names can point to the same function object. In the example, the name g inside make_prod and the name doubler in the main program both point to one function. 42:29
- Returning a function lets you hold onto it and run it later. This gives you control over the order in which a program does its work, which helps in large programs. 47:13
- Testing and debugging are mostly common sense, but learning from your own trial and error makes the advice easier to appreciate. 49:24
Words to know
- function object
- A function treated like any other value, so it can be returned, saved in a variable, and called later.
- scope
- The area of a program where a name is visible and can be used, such as inside one function or at the top level.
- decomposition
- Breaking a big problem into smaller pieces, such as separate functions, so each piece is easier to write and read.
- flow of control
- The order in which a program runs its steps, including when a function starts and when it finishes.
Example
Think of a vending machine that gives you a ticket for a snack instead of the snack itself. You hold the ticket now and use it later, when you are ready. Returning a function works the same way: you get the function back now and decide when to run it.
3 checked practice questions for this part are in the app.
Part 6 · 50:00–1:00:00
This part teaches how to test code by writing down inputs and expected outputs, how to check for bugs after fixing them, and two ways to pick good test cases.
Key ideas
- Write comments and specifications while you write the code, not after. They help you now and when you come back to the code days later. 50:20
- Testing means choosing input test cases, writing down the output you expect for each one, and checking that the real output matches. Debugging starts when a test does not match. 51:24
- Regression testing means rerunning the old tests after you fix a bug. This makes sure the fix did not break something that worked before. 54:41
- Black box testing uses only the docstring (the written description of what the function should do) to pick tests. You do not look at the code inside. 57:34
- Glass box testing uses the code itself to pick tests. You make sure every path through the code is tested, including both sides of each if/else and a case where a loop does not run at all. 59:21
Words to know
- unit testing
- Testing one small function on its own with many different inputs.
- regression testing
- Rerunning old tests after a change, to make sure the change did not break anything that already worked.
- black box testing
- Testing a function by looking only at its written description, not at how it is built inside.
- glass box testing
- Testing a function by reading its code and making sure every path through it gets tested.
Example
Suppose the same five lines of code are copied into five places in a program. If you fix a mistake in one copy, you may forget the other four, and the bug stays in them. If you put those lines in one function and call it five times, you only need to fix it once.
3 checked practice questions for this part are in the app.
Part 7 · 1:00:00–1:10:00
This part explains how to test loops and conditionals more thoroughly, and gives a systematic way to find bugs when a test fails.
Key ideas
- When you test code by looking at its paths, write tests so the loop runs zero times, one time, and many times. Each of these cases can reveal a different bug. 1:00:00
- Test the edges, called boundary conditions, not just the middle values. The absolute value example shows that testing 2 and -2 can hide a bug that appears at -1. 1:00:20
- Logic errors are the hardest kind. The line of code may look fine, and the problem only shows up when the output does not match what you expected. 1:03:36
- Debugging should be systematic, not random. Look at the failed tests for something they have in common, form a guess, and check it with another test. 1:05:49
- The bisection method means checking values halfway through the code. If the values are right there, the bug is in the second half. If they are wrong, the bug is in the first half, and you repeat the process. 1:06:53
Words to know
- glass box testing
- Writing tests by looking inside the code and making sure each path through it, like each if-branch or loop, gets tested.
- boundary condition
- A value right at the edge of a rule, such as the exact number where an if-statement switches from one answer to another. These edge values are often where bugs hide.
- logic error
- A bug where the program runs without crashing but gives the wrong answer. You cannot spot it by looking at one line alone.
- bisection method
- A way to find a bug by splitting the code in half again and again. You check the middle, then go to the half that still has the problem.
Example
Imagine a 100-page report with one wrong number somewhere. You check page 50. If it is right, the mistake must be in pages 51 to 100, so you check page 75 next. Each check cuts the search area in half, so you find the mistake much faster than reading every page.
3 checked practice questions for this part are in the app.
Part 8 · 1:10:00–1:15:33
This part shows how to find and fix a bug by using print statements to check values step by step, then retesting the code that already worked.
Key ideas
- Start debugging with the simplest input that fails. The lesson uses the short word "ab" instead of a long list, which makes the problem easier to see. 1:10:00
- A print statement shows the value of a variable at one point in the code. You compare what you see with what you expected, and any difference is a clue. 1:10:21
- Add more print statements to narrow down the problem. Here, the values were correct before the reverse line but wrong after it, so the reverse line was the problem. 1:12:31
- The reverse was missing parentheses. Reverse is a function, so it has to be called with () to run. Changing temp to a reversed copy also changed x, because temp = x made both names point to the same list. Making a copy of x fixes this. 1:13:55
- After a fix, run the original test case that already worked. This checks that the change did not break anything else. 1:15:01
Words to know
- print statement
- A line of code that displays the current value of something on screen, so you can see what the program is doing at that moment.
- parentheses
- The round brackets ( ) you write after a function's name to tell Python to run it. Without them, you only refer to the function and do not run it.
- mutable
- Able to be changed after it is created. A list is mutable, so you can add, remove, or reorder its items.
- alias
- A second name that points to the same object in memory. Changing the object through one name also changes it through the other name.
Example
Think of a shared notebook. If you write 'copy this page' by just giving a friend the same notebook, any change they make also shows up in your copy. Making a real photocopy means each of you can change your own version without affecting the other, which is what copying x does here.
3 checked practice questions for this part are in the app.
← Lecture 11: Aliasing and Cloning · Lecture 13: Exceptions and Assertions →
Notes written by AI (Claude) from the lecture's official transcript, so check anything important against the lecture. MIT OpenCourseWare · CC BY-NC-SA 4.0 · not affiliated with or endorsed by MIT. These notes are shared under the same license.
LessonBound · Free lecture notes · Privacy · Terms · New York