← Back to the Introduction to Computer Science outline The Course Maker
← Introduction to Computer Science outline
Week 15 · Chapter

Chapter 15 — Introduction to Object-Oriented Programming

Introduction to Computer Science Free sample edition

The module's primary reading · ~40–50 minutes · Objective 8 (implement and trace linear search, binary search, and selection sort; describe how an algorithm's step count grows with input size in Big-O terms; write and trace a simple recursive function; and define a class with init, attributes, and methods) and Objective 5 (define and call functions with parameters, arguments, and return values, distinguish return from print, and reason correctly about local and global scope) · Read it first this week; the lecture, the slides, the tutorial, the quiz, and the Coding Lab all build on it.


In plain language

Your reading log keeps a goal and a page count, and a separate function works them out. This week you bundle the numbers and the work into one thing. A class (a blueprint that says what data each new thing holds and what it can do) lets you build as many objects (separate things made from that blueprint) as you need. Each object keeps its own numbers, so changing one never changes another. As always, you predict what each program prints and then run it to check.

Why you already care about this

Picture an apartment building with 40 units, all built from one floor plan. The floor plan is a single sheet of paper. Nobody lives in it. But every unit built from it has its own kitchen, its own front door, and its own furniture, and moving a couch in unit 12 does not move a couch in unit 31.

Or picture two step-counter widgets in an illustrative fitness app, one on your phone and one on a friend's. Both were made by the same app, so both have the same buttons. One shows 4,000 steps and the other shows 9,000, because each widget keeps its own count.

Your reading log has been heading here all term. In Week 7 you wrote pages_left(goal, pages_read) and handed it both numbers every time you called it. This week the goal and the pages read live inside one object, mine, and the object knows how to work out its own pages left. Build a second object, yours, and it keeps a separate count. The pinned run prints 75 225 for mine and 45 105 for yours.

By the end of this chapter you will be able to:

  • write a class with __init__, attributes stored on self, and methods;
  • build two objects from one class and predict each one's attribute values;
  • explain why the caller never writes self in a method call;
  • tell a method that returns a value from one that only prints;
  • show by running that each instance keeps its own state, and that a second name for one object is an alias;
  • find the missing-self bug, whether it raises AttributeError or prints a wrong value with no error at all.

Carry this line with you: a class is the plan, not the thing; each object keeps its own state.

Where we are

  • Already covered: Week 7 gave you def, parameters, return versus print, and the frame each call opens. Week 9 gave you lists, methods such as append, aliases, and the is operator. Week 11 gave you string methods such as .upper(). Week 12 taught you to name an error by its type. Week 14 showed that every call, even a call to the same function, gets its own frame.
  • This week: classes and objects, __init__, attributes stored on self, methods, each instance keeping its own state, and the missing-self bug.
  • How they connect: a method is a Week 7 function that lives inside a class, and mine.add_day(45) uses the same dot you have used since daily.append(25) in Week 9.
  • Where we're going next: no new module follows this one. Week 16 holds the cumulative final, which draws on every objective of the course, from the first print to this week's classes.

Three earlier ideas carry most of the weight. From Week 7, a parameter is a name in the def line that receives a value when the function is called, and a function with no return hands back None. From Week 9, b = a makes a second name for the same list, not a copy. From Week 12, the last line of an error names its type. Spend extra time on Sections 5 and 8. The quiz leans on both, and so does the Coding Lab.

1. Bundle data with its methods: classes and objects

A recipe card for pancakes is not a pancake. You can cook from it one morning and again a few days later, and you get two separate stacks. If you put blueberries in the second stack, the first stack does not change. The card is the plan; each stack is a thing made from it.

Programs work the same way. Object-oriented programming is a way of organizing a program around objects, each of which bundles its own data with the methods that work on that data, instead of keeping the numbers in loose variables and the functions somewhere else.

  • A class is a blueprint for a kind of object, written with the keyword class, that says which attributes each new object starts with and which methods it can run, such as class ReadingLog.
  • An object is one thing built from a class, with its own attribute values and the methods its class defines; strings and lists are objects too, which is why they have methods such as upper and append.
  • An instance is another word for an object, used to stress which class built it: mine = ReadingLog(300) makes mine an instance of ReadingLog, and every call to the class makes a new, separate instance.

You have used objects all term without the word. Every time you wrote a dot after a value, you called a method that belongs to that value.

Worked example — objects all term. Predict both lines.

book = "the river book"
print(book.upper())
daily = [12, 40]
daily.append(25)
print(daily)

Output

THE RIVER BOOK
[12, 40, 25]

The string book is an object, and .upper() is one of its methods; it handed back a new, upper-case string. The list daily is an object too, and .append(25) changed that one list. In both lines, the value on the left of the dot is the object, and the name on the right is a method that object knows how to run. This week you write the class yourself.

Misconception check. Students often think the class is the object, so that writing class ReadingLog: creates a reading log. That is tempting, because the class is the code you can see. But a class is only the plan, like the recipe card; no object exists until a line calls the class, such as mine = ReadingLog(300). The cure: say "the class" for the plan and "an object" or "an instance" for each thing built from it.

Memory hook: the class is the recipe card; each object is a stack of pancakes.

2. Build two objects from one class

A tally counter, the little clicker a museum guard uses to count visitors, has one button and one number. Give two guards one clicker each, and each clicker keeps its own count. The smallest useful class works the same way.

Worked example — two counters. This is the pinned counter program. Predict the last line.

class Counter:
    def __init__(self):
        self.count = 0

    def increment(self):
        self.count = self.count + 1

c1 = Counter()
c2 = Counter()
c1.increment()
c1.increment()
c2.increment()
print(c1.count, c2.count)

Output

2 1

Here is what happens, line by line.

  1. The class Counter: block defines the blueprint. Nothing runs yet, the same way def does not run a function.
  2. c1 = Counter() builds a new object. Python runs the setup method named __init__, which sets that object's count to 0. Section 3 looks at __init__ closely.
  3. c2 = Counter() builds a second, separate object, also starting at 0.
  4. c1.increment() runs twice, so c1 counts 0 + 1 = 1 and then 1 + 1 = 2.
  5. c2.increment() runs once, so c2 counts 0 + 1 = 1.
  6. The last line prints both counts: 2 and 1.

In words: one class built two objects, and each object kept its own count.

Misconception check. Students often think two objects from the same class share one copy of each value, so the last line should print 3 3. That is tempting, because both objects came from one blueprint. But each call to Counter() builds a new object with its own count, and c1.increment() changes only c1. The cure: ask "which object is on the left of the dot?" before you predict a change.

In practice (your tools). Paste the counter program into Python Tutor at pythontutor.com and click Next one step at a time. Python Tutor draws each object as its own box with its own count inside, and you can watch only the box for c1 change on the first two calls.

Memory hook: two objects, two counts.

3. Watch the setup method run for you

When you buy a new phone, it powers on already set up: the clock is set, the battery level is showing, and the photo album is empty. You did not run a setup program by hand; it ran when the phone was made. A class has a setup method that works the same way.

__init__ is the method Python runs automatically, once, on each new object when the class is called, so that the object starts with its attributes set; the program never calls it by name. Its name has two underscores before and two after the word init.

Calling the class, ReadingLog(300), does two things. It makes a new object, and then it runs __init__ on that object, handing in 300 as goal.

Worked example — __init__ runs by itself. This version of ReadingLog prints a line inside __init__ so you can see when it runs. Predict all three lines.

class ReadingLog:
    def __init__(self, goal):
        print("building a log with goal", goal)
        self.goal = goal
        self.pages_read = 0

mine = ReadingLog(300)
yours = ReadingLog(150)
print(mine.goal, yours.goal)

Output

building a log with goal 300
building a log with goal 150
300 150

Nowhere does the program write __init__(...) as a call. Yet the line inside it printed twice, once for each object, because each call to ReadingLog ran it. The value in the parentheses of ReadingLog(300) went to the goal parameter, which is the parameter after self.

Misconception check. Students often think __init__ is something they must call themselves, as in mine.__init__(300). That is tempting, because every other method this week is called by name. But Python calls __init__ for you each time the class is called, as the two printed lines show. The cure: build an object by calling the class, ReadingLog(300), and let __init__ run on its own.

In practice (your tools). Run the program in your free browser Python runner. Then add a third line, theirs = ReadingLog(500), predict how many "building" lines print, and run it again. The Python Tutorial's section 9.3.2, Class Objects, on docs.python.org, describes __init__ running on each new instance.

Memory hook: call the class; Python runs __init__.

4. Store values on self: attributes

A luggage tag tied to a suitcase travels with that suitcase. A note on a scrap of paper in your pocket does not. Inside __init__, the word self is the suitcase, and writing self. in front of a name ties the value to the object.

An attribute is a value stored on an object under a name, such as self.goal inside a class or mine.goal outside it; each instance holds its own attribute values.

Dot notation is writing an object's name, a dot, and then the name of one of its attributes or methods, as in mine.pages_read or mine.add_day(45), to reach that part of that particular object.

Worked example — attributes start in __init__. Here is the full reading log class, with two methods you will meet in Section 5. Predict the last line.

class ReadingLog:
    def __init__(self, goal):
        self.goal = goal
        self.pages_read = 0

    def add_day(self, pages):
        self.pages_read = self.pages_read + pages

    def pages_left(self):
        return self.goal - self.pages_read

mine = ReadingLog(300)
print(mine.goal, mine.pages_read)

Output

300 0

Inside __init__, self.goal = goal stored 300 on the new object under the name goal, and self.pages_read = 0 stored 0 under the name pages_read. Outside the class, the object's name is mine, so the same attributes are mine.goal and mine.pages_read. Inside, the object is self; outside, it is whatever name you gave it.

Misconception check. Students often think a value assigned anywhere inside the class is stored on the object, so goal = goal would work as well as self.goal = goal. That is tempting, because both lines sit inside __init__. But a plain name inside a method is a local variable, exactly like a local in a Week 7 function, and it vanishes when the method ends. The cure: write self. in front of every value the object should remember.

In practice (your tools). Step through this program in Python Tutor. After mine = ReadingLog(300), the object's box shows two attributes, goal and pages_read, and the name mine points at that box.

Memory hook: if it is not on self, it is not on the object.

5. Pass self: methods take the object first

A barista asks "whose order is this?" before writing on a cup. Every method has the same first question: which object am I working on? The answer arrives in the first parameter.

self is the conventional name of the first parameter of every method, which receives the object the method was called on; Python passes it automatically, so the caller never writes it in the call.

When you write mine.add_day(45), Python hands mine to self and 45 to pages. The call has one value in its parentheses, and the method has two parameters; the object on the left of the dot fills the first one.

Worked example — Python passes self. Using the ReadingLog class from Section 4, predict the last line.

class ReadingLog:
    def __init__(self, goal):
        self.goal = goal
        self.pages_read = 0

    def add_day(self, pages):
        self.pages_read = self.pages_read + pages

    def pages_left(self):
        return self.goal - self.pages_read

mine = ReadingLog(300)
mine.add_day(45)
ReadingLog.add_day(mine, 30)
print(mine.pages_read)

Output

75

The first call used dot notation. The second wrote the same call out by hand: the class name, the method, and then the object as the first value. Both calls added to the same attribute, so the page count went from 0 to 45 and then to 75, since 45 + 30 = 75. You will almost always write the first form; the second shows what Python does for you.

Worked example — passing self by hand. Predict what happens.

class ReadingLog:
    def __init__(self, goal):
        self.goal = goal
        self.pages_read = 0

    def add_day(self, pages):
        self.pages_read = self.pages_read + pages

    def pages_left(self):
        return self.goal - self.pages_read

mine = ReadingLog(300)
mine.add_day(mine, 45)
print(mine.pages_read)

Output

TypeError

Nothing printed. Python already passed mine as self, so the call handed in three values, mine, mine, and 45, to a method with two parameters, and the run stopped with TypeError before the print line.

Misconception check. Students often think self is a value the caller passes, so a call should read mine.add_day(mine, 45). That is tempting, because self is written in the def line like any other parameter. But the object on the left of the dot is already the first value, so passing it again is one value too many. The cure: write one value in the call for each parameter after self.

Caveat: the name self is a convention, not a keyword. Python would accept any name for the first parameter, but every Python programmer writes self, and so should you.

In practice (your tools). Run the "passing self by hand" program in your browser runner and read the last line of the error: TypeError. Then delete the extra mine, and run it again to see 45. The Python Tutorial's section 9.4 on docs.python.org explains that self is only a naming convention.

Memory hook: the object before the dot is self.

6. Report or return: keep print apart from return

A kitchen timer can ring a bell, or it can hand you a number you write down. Only the number can be used later. Methods make the same choice that functions made in Week 7.

A method is a function that lives inside a class, so everything about return and print still holds. A method that ends with return hands a value back to the line that called it. A method that only prints shows something on the screen for a person and hands back None.

Worked example — return versus print. This ReadingLog adds a report() method that prints. Predict all three lines.

class ReadingLog:
    def __init__(self, goal):
        self.goal = goal
        self.pages_read = 0

    def add_day(self, pages):
        self.pages_read = self.pages_read + pages

    def pages_left(self):
        return self.goal - self.pages_read

    def report(self):
        print(self.pages_read, "of", self.goal)

mine = ReadingLog(300)
mine.add_day(45)
result = mine.report()
print(result)
print(mine.pages_left() // 30)

Output

45 of 300
None
8

Here is the reasoning.

  1. result = mine.report() runs the method, which prints 45 of 300.
  2. report() has no return, so it hands back None, and print(result) shows it.
  3. pages_left() returns 300 − 45 = 255, a value the program can use in an expression.
  4. 255 // 30 is 8: eight full days of reading at 30 pages a day fit inside the pages that are left.

In words: the printing method showed text and gave the program nothing; the returning method gave the program a number to calculate with.

Worked example — not a plain function. Predict what happens.

class ReadingLog:
    def __init__(self, goal):
        self.goal = goal
        self.pages_read = 0

    def add_day(self, pages):
        self.pages_read = self.pages_read + pages

    def pages_left(self):
        return self.goal - self.pages_read

mine = ReadingLog(300)
add_day(45)

Output

NameError

The method add_day lives inside the class, not at the top level of the program, so the bare name add_day was never assigned there. The run stopped with NameError. Call it through an object, mine.add_day(45).

Misconception check. Students often think methods are ordinary functions that can be called anywhere by name. That is tempting, because a method is written with def, like a function. But a method belongs to its class and needs an object to work on, so it is reached through dot notation. The cure: put the object and a dot in front of every method call.

In practice (your tools). Ask your approved chatbot whether print(mine.report()) prints a number. Then run the program above and compare: the method printed its line, and the call itself handed back None. The Python Tutorial's section 4.8 on docs.python.org says a function with no return hands back None, and a method is no exception.

Memory hook: print shows a person; return hands the program a value.

7. Run the reading log as a class: each instance keeps its own state

Two friends start the same book club challenge on the same day. One aims for 300 pages and the other for 150. Each tracks their own progress, and one friend's busy weekend does not change the other's count.

State is the current values of all of an object's attributes, such as 75 pages read toward a goal of 300; methods can change an object's state, and changing one instance leaves every other instance as it was.

Worked example — the reading log's Week 15 stage. This is the pinned program. Predict both lines.

class ReadingLog:
    def __init__(self, goal):
        self.goal = goal
        self.pages_read = 0

    def add_day(self, pages):
        self.pages_read = self.pages_read + pages

    def pages_left(self):
        return self.goal - self.pages_read

mine = ReadingLog(300)
yours = ReadingLog(150)
mine.add_day(45)
mine.add_day(30)
yours.add_day(45)
print(mine.pages_read, mine.pages_left())
print(yours.pages_read, yours.pages_left())

Output

75 225
45 105

Here is the arithmetic.

  1. mine started at 0 pages and added 45 and 30, so 45 + 30 = 75 pages read, and 300 − 75 = 225 pages left.
  2. yours started at 0 pages and added 45, so it read 45, and 150 − 45 = 105 pages left.
  3. Adding to yours never touched mine. Each object kept its own state.

Worked example — an alias, not a copy. In Week 9, b = a gave one list a second name. The same is true of any object. Predict the last line.

class ReadingLog:
    def __init__(self, goal):
        self.goal = goal
        self.pages_read = 0

    def add_day(self, pages):
        self.pages_read = self.pages_read + pages

    def pages_left(self):
        return self.goal - self.pages_read

mine = ReadingLog(300)
mine.add_day(45)
same = mine
same.add_day(30)
print(mine.pages_read, same is mine)

Output

75 True

same = mine built no new object. It gave the one object a second name, so same.add_day(30) changed the object that mine names too, and is confirms that both names point at one object. To get a second log, call the class again.

Worked example — a method that decides. An illustrative bike-share dock starts with 2 bikes. Its rent() method lends a bike and returns True when one is left, and returns False when the dock is empty. Predict both lines.

class BikeDock:
    def __init__(self, bikes):
        self.bikes = bikes

    def rent(self):
        if self.bikes > 0:
            self.bikes = self.bikes - 1
            return True
        return False

north = BikeDock(2)
print(north.rent(), north.rent(), north.rent())
print(north.bikes)

Output

True True False
0

The first two rentals found a bike, so the count went 2, then 1, then 0, and each call returned True. The third found the count at 0, skipped the if block, and returned False. The dock's state, its bikes attribute, changed with every successful rental.

Misconception check. Students often think that same = mine makes a second reading log, because it looks like making a copy. That is tempting, because two names usually mean two things. But assignment never builds an object; only calling the class does. The cure: count the calls to the class. One call, one object, however many names point at it.

In practice (your tools). Step through the alias program in Python Tutor. Watch two arrows, one from mine and one from same, point at a single object box. Then step through the pinned stage and watch two separate boxes appear.

Memory hook: one call to the class, one object.

8. Find the missing-self bug

You fill out a change-of-address form and then leave it on your own desk. The address in your head changed, but the post office never heard. A method that forgets self. does the same thing: it works out a value and never stores it on the object.

The missing-self bug is the mistake of assigning to a plain name such as name = name inside a method instead of to self.name, so nothing is stored on the object. It shows up later as an AttributeError or as a value that never changes.

AttributeError is the exception Python raises when a program asks an object for an attribute or method it does not have, such as reading self.name when no line ever stored a name on that object.

Worked example — the loud version. This is the pinned missing-self program. Predict what happens.

class Cat:
    def __init__(self, name):
        name = name

    def speak(self):
        return self.name + " says Meow"

c = Cat("Milo")
print(c.speak())

Output

AttributeError

name = name assigned the parameter to itself, a local name that vanished when __init__ ended. Nothing was stored on the object. Then speak() asked for self.name, the object had no such attribute, and the run stopped with AttributeError. The repair is one word.

class Cat:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return self.name + " says Meow"

c = Cat("Milo")
print(c.speak())

Output

Milo says Meow

Worked example — the silent version. This time self. is missing on the left side of the line inside add_day. Predict the last line.

class ReadingLog:
    def __init__(self, goal):
        self.goal = goal
        self.pages_read = 0

    def add_day(self, pages):
        pages_read = self.pages_read + pages

mine = ReadingLog(300)
mine.add_day(45)
print(mine.pages_read)

Output

0

No error appeared at all. The method worked out 0 + 45 = 45, stored it in a local name, and threw it away when the method ended, so the attribute stayed at 0. A program that runs and prints the wrong thing has a logic error, which is harder to spot than a traceback.

Worked example — check a chatbot's claim. Suppose a chatbot tells you, "Two objects of one class share the same pages_read, so adding to yours also adds to mine." Run the reading log's Week 15 stage from Section 7. It printed 75 225 and 45 105, two different counts, so the claim was wrong. The computer does exactly what you wrote, not what you meant, and not what a chatbot says it will do.

Misconception check. Students often think data can be stored without self., because the line name = name looks like it saves the name. That is tempting, because the line sits inside the class. But without self. the name is local to the method, as the AttributeError and the stuck 0 show. The cure: when an attribute is missing or never changes, look for a line inside a method that forgot self. on its left side.

In practice (your tools). Run the loud version in your browser runner and read the last line of the traceback. Then step through the silent version in Python Tutor: the local pages_read appears in the method's frame, holds 45, and disappears when the frame closes, while the object's box still shows 0.

Memory hook: a missing self. is a value with nowhere to live.

9. Look back: the course's path and the final ahead

A hiking trail map shows the whole path at the end of a long walk, and only then do the early turns make sense. Your reading log is that map for this course. Every week added one idea to the same small program.

Worked example — the reading log's path. The table below lists some of the pinned stages and what each one printed. Read down the rows and name the idea each week added.

Selected stages of the running reading log, the idea each added, and the pinned output of each
Week What the stage added Pinned output
1 one print Reading log: day 1
2 variables and arithmetic 255, 8.5, 8, 15
5 a while loop with a counter 9, 315
7 the function pages_left(goal, pages_read) 255, 0, 5
9 the daily list [12, 40, 25, 0, 33, 17, 27] 154, 22.0, 40, 7
12 the log saved to a file and read back 77
14 the daily list summed recursively 154, 0
15 the class ReadingLog with two instances 75 225, 45 105

The first row printed one line of text. The last row built two objects, each carrying its own numbers and its own methods. In between, every idea you met, from int() to try and except, is still at work inside the methods of a class. A method body is ordinary Python: assignment, arithmetic, if, loops, and return.

Misconception check. Students often think object-oriented programming replaces the programming they learned before it, so a class needs a whole new way of thinking. That is tempting, because the vocabulary is new. But look inside any method this week: it is the same assignment, arithmetic, and return from Weeks 2 through 7. The cure: read a class as a bundle of functions that share one set of attributes, and trace each method the way you have traced every function this term.

In practice (your tools). Open the Python Tutorial's chapter 9, Classes, on docs.python.org, and skim the section titles. You now know enough to read its first examples; the later sections cover ideas from a second course.

Honesty note: this chapter covers only the class tools Week 15 needs. Inheritance, polymorphism, class variables, and the other special methods besides __init__ come in a second course.

Week 16 brings no new material. It holds the cumulative final, which draws on every objective of the course. The habit that carries you through it is the one you have practiced since Week 1: predict, then run.

Pause and choose one of these to think about before you read on:

  • (a) Look back at the reading log's path in the table. Which week's idea changed how you read a program the most, and what could you not do before it?
  • (b) Think of an app you use that keeps many similar things, such as contacts, playlists, or saved places. If each one were an object, what attributes would it hold, and what methods would it need?

Memory hook: predict, then run, from the first print to the last class.

Key terms

  • object-oriented programming — A way of organizing a program around objects, each of which bundles its own data with the methods that work on that data, instead of keeping the numbers in loose variables and the functions somewhere else. (first used: 1. Bundle data with its methods: classes and objects)
  • class — A blueprint for a kind of object, written with the keyword class, that says which attributes each new object starts with and which methods it can run, such as class ReadingLog. (first used: 1. Bundle data with its methods: classes and objects)
  • object — One thing built from a class, with its own attribute values and the methods its class defines; strings and lists are objects too, which is why they have methods such as upper and append. (first used: 1. Bundle data with its methods: classes and objects)
  • instance — Another word for an object, used to stress which class built it: mine = ReadingLog(300) makes mine an instance of ReadingLog, and every call to the class makes a new, separate instance. (first used: 1. Bundle data with its methods: classes and objects)
  • __init__ — The method Python runs automatically, once, on each new object when the class is called, so that the object starts with its attributes set; the program never calls it by name. (first used: 3. Watch the setup method run for you)
  • attribute — A value stored on an object under a name, such as self.goal inside a class or mine.goal outside it; each instance holds its own attribute values. (first used: 4. Store values on self: attributes)
  • dot notation — Writing an object's name, a dot, and then the name of one of its attributes or methods, as in mine.pages_read or mine.add_day(45), to reach that part of that particular object. (first used: 4. Store values on self: attributes)
  • self — The conventional name of the first parameter of every method, which receives the object the method was called on; Python passes it automatically, so the caller never writes it in the call. (first used: 5. Pass self: methods take the object first)
  • state — The current values of all of an object's attributes, such as 75 pages read toward a goal of 300; methods can change an object's state, and changing one instance leaves every other instance as it was. (first used: 7. Run the reading log as a class: each instance keeps its own state)
  • missing-self bug — The mistake of assigning to a plain name such as name = name inside a method instead of to self.name, so nothing is stored on the object and the value is lost when the method ends. (first used: 8. Find the missing-self bug)
  • AttributeError — The exception Python raises when a program asks an object for an attribute or method it does not have, such as reading self.name when no line ever stored a name on that object. (first used: 8. Find the missing-self bug)

Summary

  • A class is the blueprint; an object, or instance, is one thing built from it.
  • Calling the class builds a new object and runs __init__ on it automatically.
  • Store each value the object should remember on self, as an attribute.
  • Every method takes self first, and Python passes the object for you.
  • A method that returns hands back a value; a method that only prints hands back None.
  • Each instance keeps its own state; a second name for one object is an alias.
  • Without self., a value is a local name: expect AttributeError or a value that never changes.
  • Check every claim about a class, including a chatbot's, by running the code.

Check your understanding

Work these before looking at the answers — they are ungraded, and the answers (with reasoning) are at the bottom of the page.

  1. A home has two rooms with thermostats (an illustrative example). A class Thermostat stores self.setting = setting in __init__, and its method warmer() adds 2 to self.setting. The program builds hall = Thermostat(18) and den = Thermostat(21), calls hall.warmer() twice and den.warmer() once, and prints hall.setting and den.setting on one line. Predict the line.
  2. A board game has a class Die whose __init__(self, sides) contains the single line sides = sides, and whose method biggest() returns self.sides. The program runs d = Die(6) and then print(d.biggest()). Predict what happens, and name the one-word fix.
  3. A campus club's class SignUpSheet starts self.count at 0, its method add() adds 1 to it, and its method show() prints signed up: followed by the count. The program calls sheet.add() three times, then runs result = sheet.show() and print(result). Predict both printed lines.
  4. Using the same SignUpSheet, a classmate writes sheet.add(sheet) because "the method's def line asks for self." Predict what happens and explain why.

Answers (worked reasoning — read after attempting)

  1. It prints 22 23. The hall started at 18 and warmed twice, 18 + 2 + 2 = 22. The den started at 21 and warmed once, 21 + 2 = 23. Each thermostat is its own object, so warming the hall never touched the den. If you predicted the same number twice, you met the "objects share one copy" trap.
  2. It prints nothing and stops with AttributeError. The line sides = sides stored the 6 in a local name that vanished when __init__ ended, so the object never got a sides attribute, and biggest() failed when it asked for self.sides. The fix is self.sides = sides, after which the program prints 6.
  3. It prints signed up: 3 and then None. Three calls to add() raise the count from 0 to 3, and show() prints it. But show() has no return, so it hands back None, and print(result) shows that.
  4. It prints nothing and stops with TypeError. Python already passes sheet as self because it is on the left of the dot, so sheet.add(sheet) hands in one value too many for a method with only the self parameter. The call should be sheet.add().

Next up in this module: the Readings & Resources page, the lecture tutorial, the practice set, and the quiz — and next week, the cumulative final asks you to trace every method you have learned, from the first print to this week's classes.

Built with The Course Maker · samples.thecoursemaker.com