Defining Your Own Types: Classes
Learning Objectives
- You can explain the limitations of using a dictionary as a record.
- You can define a class with an
__init__method that sets up its attributes. - You can create instances of a class and read their attributes with dot notation.
- You can distinguish attribute access (
session.minutes) from key access (session["minutes"]).
A dictionary fixed the main problem with list-of-rows: the fields have names now. But a dictionary is still a loose collection of keys; a typo in a key is not an error, and there is no guarantee that every session has the same keys. The behaviour that operates on sessions is still separate from the data, and there is no way to say “this is what a session is” in one place.
This chapter introduces classes, which let you define your own type — a Session that is always shaped like a session. This chapter is the third and last step of the part’s arc from row[1] to session.minutes.
A dictionary is the right tool when the keys genuinely vary, or when you are assembling data whose shape you do not control. But a session has a fixed shape — every session has exactly a subject, a number of minutes, and a date — and for fixed-shape records, classes are a better fit than dictionaries.
Defining a Class
A class defines a new type. Writing a class is writing down, in one place, what every value of that type looks like. Here is a Session:
class Session: def __init__(self, subject, minutes, date): self.subject = subject self.minutes = minutes self.date = dateThere is a fair amount of new syntax here, so take a moment to read it through.
class Session: begins the definition of a new type called Session. By convention, class names start with a capital letter, which is how you tell Session (a type) from session (a value of that type).
Inside, def __init__(self, subject, minutes, date): defines a special method — in some languages called a constructor — that runs automatically whenever you create a new Session. Its job is to set up the new object’s data. The name __init__ — short for “initialise”, with two underscores on each side — is special: Python looks for exactly that name and calls it for you. You do not call it directly.
The first parameter, self, is the object being created. Every method’s first parameter is self, and it refers to the particular object the method is working on. When __init__ runs, self is the new, blank Session, and the three assignments fill it in:
self.subject = subjectself.minutes = minutesself.date = dateEach line takes one of the values passed in and stores it on the object under a name. self.subject = subject means “store the subject argument on this object, as its subject attribute.” The name on the left (after self.) is the attribute; the name on the right is just the parameter. They are spelled the same here by convention, but they are different things — one is a slot on the object, the other is an argument that arrived.
The role of self
0 / 5 points
Inside a constructor such as def __init__(self, title, priority):, what does self refer to?
Creating an Instance
With the class defined, you create a Session by calling the class name like a function, passing the values that __init__ expects — but not self, which Python supplies automatically:
session = Session("math", 45, "2024-03-10")This creates a new Session object — an instance of the class — and runs __init__ with self bound to the new object and the other three parameters set to "math", 45, and "2024-03-10". When __init__ finishes, session is a fully set-up Session.
You read its data with dot notation:
print(session.subject) # mathprint(session.minutes) # 45print(session.date) # 2024-03-10session.minutes means “the minutes attribute of session.”
Attribute access
0 / 5 points
A Ticket object stores its priority as an attribute named priority.
Which expression reads that value?
Now, you know the three ways of accessing data, each from a different kind of structure:
row[1] # positional from listssession["minutes"] # key access from dictionariessession.minutes # attribute access from classesNested Structures
A class can also hold more complex data. For example, a StudyLog class could hold a list of Session objects as its sessions attribute.
class StudyLog: def __init__(self, sessions): self.sessions = sessionsThe pattern for accessing the data is the same: you read the sessions attribute of a StudyLog instance, and then you can read the attributes of each Session in that list:
log = StudyLog([Session("math", 45, "2024-03-10")])print(log.sessions[0].subject) # mathBenefits from a Class
A class makes the expected shape explicit and checks construction.
The class definition says, in one place, that a Session has a subject, minutes, and a date. Anyone reading the code can look at __init__ and know exactly what a session is. With a dictionary, the shape was implied by usage scattered across the program; now it is stated.
This means that mistakes surface sooner. A misspelled attribute is an error at the moment you use it, not a silent default much later:
print(session.mintues)# AttributeError: 'Session' object has no attribute 'mintues'Compare this to session.get("mintues", 0) on a dictionary, which would have quietly returned 0 and let the bug travel. The class catches the typo loudly, near where it was made.
Similarly, construction of an instance is checked. Creating a Session requires exactly the arguments __init__ declares. Leave one out and Python complains immediately:
Session("math", 45)# TypeError: __init__() missing 1 required positional argument: 'date'You cannot accidentally build a session that is missing its date, the way you could accidentally build a dictionary without a "date" key. The type enforces its own shape.
Classes can also have methods, which are functions that run on an instance and can access its attributes. With a class, you can say “this is what a session is, and this is what you can do with a session” in one place. Methods are the subject of the next chapter.
Check Your Understanding
- Name two things about a record’s shape that a dictionary does not guarantee but a class does.
- What is the role of
selfin the__init__method? - When you write
Session("math", 45, "2024-03-10"), why do you not pass a value forself? - What happens if you read
session.mintues(misspelled) on aSessionobject? How does that differ fromsession.get("mintues", 0)on a dictionary? - A class is a good fit for a session but not necessarily for data whose set of keys varies from record to record. Why?
AI Study Aid
Create a chapter diagram
We're looking into to what extent AI-generated diagrams could help with studying.
Use this study aid to generate an AI-generated visual summary of the material you just studied.
Each diagram style emphasizes the content in a different way, so you can choose the focus
that feels most useful.
Using the diagram generator is voluntary and does not affect your course progress. We encourage
you to try it out and see if it helps, but it's totally up to you! Your answers help us understand
how to make better study aids in the future.