NOETRION

Choose a topic

DAY 19 OF 30 · OBJECTS AND USEFUL TOOLS

Classes, objects, and methods

Represent a small domain object and connect its state to behavior without unnecessary abstraction.

90–120 minutes suggested study timePython 3.12Not yet completed

Today's outcomes

  • Distinguish a class from an instance.
  • Read attributes and call methods.
  • Explain how self identifies the receiving object.

Before you start: Complete Day 18 and its practical challenge.

Understand the concept

A class describes a kind of object

A class defines behavior and a namespace for a kind of object. An instance is one concrete object created from that class. Two study-session instances can share the same method definitions while holding different topics and minute counts.

Classes are useful when several operations belong with a coherent state. They are not required for every script. A pure function that calculates a mean from a list remains a good design. Introduce an object when it makes state ownership or an invariant clearer, rather than wrapping every number in a class.

The syntax class StudySession: creates the class, and StudySession() creates an instance using the class's construction behavior. Today we assign instance fields after creation so you can see the relationship directly. Day 20 moves that work into initialization, preventing half-prepared objects from reaching methods that require their fields.

Attributes belong to objects; methods operate through an instance

An attribute is accessed with a dot, such as session.minutes. A method is a function defined on the class and normally called through an instance. In session.describe(), Python supplies the receiving instance to the method's first parameter, conventionally named self.

self is not a keyword with automatic magic independent of the call; it is a parameter name. The convention makes methods easy to recognize. Within a method, self.minutes refers to a field on that receiving object. A bare minutes name would be a different local or global name, not automatically the instance's field.

Methods can observe or change state

A describe method can return a report without changing the object. A record method can update the object deliberately. Make that difference part of the interface. If record returns None, the caller should not assign its result back to the object expecting an updated instance.

State-changing methods should preserve the object's rules. A study goal cannot accept a negative practice duration under today's contract. Validate before updating so a rejected operation leaves state unchanged. Object-oriented syntax does not replace explicit validation or tests.

Worked example

class StudySession:
    def describe(self):
        return f"{self.topic}: {self.minutes} minutes"

    def add_minutes(self, extra):
        if type(extra) is not int or extra < 0:
            raise ValueError("extra minutes must be a nonnegative integer")
        self.minutes += extra

first = StudySession()
first.topic = "functions"
first.minutes = 30
second = StudySession()
second.topic = "lists"
second.minutes = 45

first.add_minutes(15)
assert first.minutes == 45
assert second.minutes == 45
assert first is not second
print(first.describe())
print(second.describe())
Object Topic Initial minutes After first.add_minutes(15)
first functions 30 45
second lists 45 45

Both objects end with equal minute values but remain distinct instances. Equality of one attribute does not establish object identity. The method selects its state through self, so updating first does not automatically update second.

Calling describe before assigning topic or minutes would fail with AttributeError. That exposes the weakness of manual preparation. It is acceptable for today's transparent demonstration, but initialization tomorrow will give the class a reliable creation contract.

Guided practice

  1. Predict: which object's minutes change when you call second.add_minutes(10)? Trace self at the method's entry and the field after assignment.
  2. Modify: add an is_full_session(target=60) method returning a Boolean without printing. Test 59, 60, and 61 and retain the inclusive threshold rule.
  3. Reconstruct: represent the same session with a dictionary and two ordinary functions. Compare how the caller supplies state in the dictionary version versus the method version. Explain which design is clearer for this small task and why.

Avoid an assumption that using classes makes a program automatically reusable, correct, or secure. The contract, boundaries, and tests still determine those qualities.

Daily challenge

Create a StudyGoal class with methods record(minutes) and remaining(). Prepare each instance with a positive target and completed_minutes zero. record accepts a nonnegative actual integer, updates completed_minutes, and returns None. remaining returns the unfinished portion, bounded below by zero. Rejected negative or Boolean input must not change state.

Action sequence for target 60 Completed Remaining
No recording 0 60
Record 30 30 30
Record another 45 75 0
Attempt -1 Still 75 Still 0

Over-target practice is allowed and retained in completed_minutes. The remaining amount becomes zero rather than negative. This is an explicit goal policy, different from the completed-day validation rule on Day 5.

Reference solution

Compare after attempting the challenge
class StudyGoal:
    def record(self, minutes):
        if type(minutes) is not int or minutes < 0:
            raise ValueError("minutes must be a nonnegative integer")
        self.completed_minutes += minutes

    def remaining(self):
        return max(0, self.target - self.completed_minutes)

goal = StudyGoal()
goal.target = 60
goal.completed_minutes = 0
assert goal.remaining() == 60
assert goal.record(30) is None
assert goal.remaining() == 30
goal.record(45)
assert goal.remaining() == 0 and goal.completed_minutes == 75
for invalid in [-1, True]:
    try:
        goal.record(invalid)
    except ValueError:
        pass
    else:
        raise AssertionError("Invalid recording should fail")
assert goal.completed_minutes == 75
print("Goal state:", goal.completed_minutes, goal.remaining())

The validation happens before the update, preserving the previous valid state. The observation method uses max to avoid a negative unfinished amount while retaining the actual accumulated practice.

Debug and explain

Symptom Investigate Likely repair
AttributeError for a required field Was the object fully prepared? Initialize the field before method use
TypeError about method arguments Does the definition include self? Match method parameters to the call
Wrong object's state changes Which instance receives the call? Trace the receiver explicitly
Object name becomes None Assigned result of a mutating method Keep the instance; inspect method return contract

Explain what the class defines, what each instance owns, and which methods change state. Then identify the creation weakness still present in the reference solution. That weakness gives tomorrow's initializer a concrete purpose.

Review and next step

Save the state trace and invalid-update checks. At the next review, create two independent goals and update only one. Day 20 replaces manual preparation with validated initialization and examines shared class attributes.

Sources and verification

Original teaching examples checked with Python 3.12.14. Library behavior reviewed against these primary references on October 3, 2026. Day 26's examples also use Beautiful Soup 4.14.3.

DAILY CHECKPOINT

Check your understanding

Five questions, 20 points each. Pass at 85% or above, which requires all five correct. Feedback explains each answer; revise the lesson and retry when needed.

01. What is an instance?

02. What does a method's self parameter refer to?

03. Why keep behavior with object state?

04. Does underscore naming enforce security or access prevention?

05. When is a plain function still appropriate?

Review your practical work

Check these statements after running your own challenge. This is your self-review; the website does not execute or assess your Python code.

Pass the knowledge check and finish the practical self-review to save completion.