Why does renaming one data attribute break every caller who reached for it directly, while the ones who went through `get_age()` never notice?
Short drills on what this video just taught. Write the code, run the checks, and reveal the answer only if you are stuck.
The Ticket class below stores a row and a seat letter, and its __str__ is empty. Give it a body so that a ticket prints as the word seat, a colon, the row, another colon, then the letter — a ticket in row 12 with letter B must display exactly seat:12:B.
Then build that very ticket, row 12 and letter B, and assign the object to ticket.
The __str__ dunder method
Write __str__ returning "animal:"+str(self.name)+":"+str(self.age), note that it returns the string rather than printing it, and record what a class without one prints instead.
Setters and the default parameter
Write the two getters and set_age, set_name(self, newname=""), then trace a = Animal(4), a.set_name("fluffy") and a bare a.set_name(), recording the printed animal:fluffy:4 and animal::4.
Method access versus direct attribute access
Record that a.age and a.get_age() both print 4, and that nobody opens the list class definition to learn its attribute names, working only through its methods.
The rename from age to years
Rewrite the class with self.years = age and the getter and setter updated to match, then state which callers keep working and which now throw an error.