14.3 Classes and objects
- Approved by
- Panaversity ·
In everyday life. A builder works from a blueprint for a house. The blueprint is not a house. Each house built from it is one real thing, with its own address and paint color. All have the same rooms.
What you will learn. How to read code built from blueprints, by its data and the job of each function.
Why it matters. Much of the code an agent writes, and most code a company publishes for its service, is built this way.
What is a class, and what is an object?
A blueprint, and one thing built from it.
See a small one work. This class is the blueprint for one invoice. Two objects are built from it, as two houses are built from one blueprint, and each holds its own number and amount. The comments name the parts. Predict both lines.
class Invoice {
// its two fields
number: string;
cents: number;
// the constructor: runs when an object is built, and fills in its fields
constructor(number: string, cents: number) {
this.number = number;
this.cents = cents;
}
// a method: "this" is the object it was called on
needsSecondApprover(): boolean {
return this.cents > 1000000; // is this one's amount over $10,000.00?
}
}
const first = new Invoice("INV-8301", 482500); // build one object
const second = new Invoice("INV-8303", 1290000); // and another, with its own data
console.log(first.number, first.needsSecondApprover());
console.log(second.number, second.needsSecondApprover());INV-8301 false
INV-8303 trueEach object holds its own cents, so the same method gives two different answers. These words name the parts.
| Part | What it is | In the code above |
|---|---|---|
| Class | A blueprint for a kind of thing | Invoice |
| Object, or instance | One thing built from it | first and second |
| Fields | The data each object holds | number, cents |
| Constructor | The method that runs when an object is built and fills in its fields | Takes a number and an amount |
| Methods | Functions that work on that object's data | needsSecondApprover() |
Inside a method, the object is this in TypeScript and self in Python.
How do I read a class I did not write?
By its fields, and by the one-sentence job of each method.
At work. The agent reorganized Brightline's scripts into two small classes. VendorDirectory looks up names by id. InvoiceBook holds the invoices and a directory, and answers questions about them with four methods: open(), openTotalCents(), dueDateOf(inv) and dueBy(cutoff). Ana says what each method does in one sentence. In dueBy, she checks that it calls open(), so paid invoices stay out, and dueDateOf, so empty due dates are computed from the terms.
Read a short version of VendorDirectory the same way: one field, and one method. The method's last line uses the or default from 13.4, and puts a value inside text, as 13.7 did. Predict both lines.
class VendorDirectory {
// its one field: the names, kept by id
#byId: Record<string, string>;
constructor(byId: Record<string, string>) {
this.#byId = byId;
}
// its one method: the name under this id, or this text if there is none
name(id: string): string {
return this.#byId[id] || `Unknown vendor ${id}`;
}
}
const directory = new VendorDirectory({ V005: "Hocking Valley Pallets" });
console.log(directory.name("V005"));
console.log(directory.name("V099"));Both print:
Hocking Valley Pallets
Unknown vendor V099Private fields. Only the class's own methods can see a private field. TypeScript marks one with #, as in #byId, and enforces it. Python marks one with a leading underscore, as in _by_id, which is only a naming habit.
Hold another object, or extend it?
Usually hold it. With inheritance, a class extends another and becomes a kind of it, taking all its methods. With composition, a class holds another object as a field and uses it. An InvoiceBook is not a kind of vendor directory. It has one. Composition keeps each class small and lets you test each one alone. Agent code sometimes reaches for inheritance first. Ask whether "is a kind of" is really true.
Why are two objects with the same data not equal?
Because the language asks whether they are the same object, unless a method compares their fields. Two houses with the same paint are still two houses, at two addresses.
| Language | What an equal test asks |
|---|---|
| TypeScript | === on two objects always asks whether they are the same object, so code compares a field, such as the vendor id. TypeScript has no dunder methods. |
| Python | == uses the class's __eq__ method, if it has one. Python names some methods with two underscores on each side. These are dunder methods, for "double underscore". Python calls __init__ when an object is built, __repr__ when it is printed, and __eq__ when it is compared with ==. On a class that only holds data, @dataclass writes all three for you. |
When an agent's test compares two records with == or ===, check which kind of equal it means.

Figure 14.3. Same data, three answers. Only an eq method, which @dataclass writes, makes two objects with the same data equal.
Try it
The file in 14-3 is the small Invoice class. Run it and investigate, as in 13.1.
Modify. Send: "Please change the second invoice's amount to exactly 1000000 cents. Don't run it yet." Say what each line will print. Then ask the agent to run it.
Make. Send: "Please add a status field to the class and a method that answers whether the invoice is open. Print the answer for one open invoice and one paid invoice. Don't run it yet." Find the new field and the new method. Say what it will print, and then ask the agent to run it.
What you should see. After your change, both invoices answer false. Exactly $10,000.00 is not over $10,000.00.
The operating rule: read a class by its fields and the one-sentence job of each method. Prefer an object that holds another to one that extends it.

Figure 14.4. Composition. The book has a directory and asks it for names. It is not a kind of directory.
Next. 14.4 decides what your program lets in.
Check yourself
Question 1 / 6
0 answered
An agent's test builds two invoices from the same row and compares them.class Invoice:
def __init__(self, number: str, amount_cents: int) -> None:
self.number = number
self.amount_cents = amount_cents
a: Invoice = Invoice("INV-5318", 64000)
b: Invoice = Invoice("INV-5318", 64000)
print(a == b, a.number == b.number)What does it print?
Sources
- Owner
- Panaversity