Checkpoint: OOP & Standard Library
A checkpoint is a consolidation stop: you recap the cluster of skills you just learned — abstract base classes, properties, __slots__ , scope, custom exceptions, zoneinfo, pickle, and configparser — and prove they fit together by building one small program that uses several at once.
Learn Checkpoint: OOP & Standard Library in our free Python course — an interactive lesson with runnable examples, a practice exercise and a quick reference.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Isolated facts fade; connected skills stick. This stop turns eight separate lessons into one working mental model you can actually reach for in real code.
The first cluster sharpened how you design classes — enforcing interfaces, managing attributes, and optimizing memory:
The second cluster covered how names resolve and how to design robust error handling:
The third cluster added three practical modules for time, persistence, and configuration:
Combine four ideas into one small system: an abstract base class Service with an abstract protocol() , a port property whose setter raises a custom exception ConfigError for invalid ports, and a concrete url() built from the protocol, name, and port.
Start from this runnable scaffold and extend it. Run it after each change:
1. What happens when you try to instantiate a subclass of an ABC that hasn't implemented every @abstractmethod ?
Python raises TypeError at instantiation: "Can't instantiate abstract class ... with abstract method ...". The class stays abstract until all required methods are implemented.
2. You add __slots__ = ("x", "y") to a class. What can you no longer do, and why?
You can no longer assign attributes that aren't listed in __slots__ — doing so raises AttributeError . There's no per-instance __dict__ to hold arbitrary new attributes, which is exactly what makes the instances smaller.
3. Inside a closure, you want an inner function to update a variable defined in the enclosing function. Which keyword do you use — global or nonlocal ?
nonlocal . It rebinds the nearest enclosing function's variable. global would reach all the way out to the module level instead, missing the enclosing scope entirely.
4. What's the difference between raise NewError(...) and raise NewError(...) from original ?
from original sets __cause__ , explicitly chaining the new exception to the one that caused it; the traceback shows "direct cause." Without from , the original is only kept implicitly as context and the link is less clear.
5. Why should times be stored in UTC and converted to a local zone only for display?
UTC has no daylight saving and never shifts, so it's an unambiguous baseline for storage and comparison. Converting to a local zone with astimezone only at display time avoids DST-related bugs.
6. A teammate wants to pickle.loads() a blob downloaded from a public URL. What's wrong, and what should they use instead?
Unpickling untrusted data can execute arbitrary code — a serious security hole. For data from an external/untrusted source they should use json (text, safe, language-neutral). Reserve pickle for trusted, Python-to-Python state.
❌ Routing __init__ around the validating setter
✅ Assign through the property name so the setter runs: self.port = port .
❌ Forgetting __slots__ = () on a slotted subclass
✅ Add an empty __slots__ = () to each subclass to keep the memory savings.
Checkpoint cleared — your OOP & stdlib skills connect now!
You recapped abstract base classes, properties, __slots__ , scope, custom exceptions, zoneinfo, pickle, and configparser — and combined four of them into a validated service registry. These aren't isolated tricks anymore; they're a toolkit you can compose.
🚀 Up next: Final Project Ideas — put everything you've learned into something real.
Practice quiz
What happens when you try to instantiate a subclass of an ABC that has not implemented every @abstractmethod?
- It instantiates but the missing method returns None
- It raises NotImplementedError when the method is called
- Python raises TypeError at instantiation
- Nothing — abstract methods are only a hint
Answer: Python raises TypeError at instantiation. Instantiating an abstract class with unimplemented abstract methods raises TypeError immediately. The class stays abstract until all required methods are implemented.
You add __slots__ = ("x", "y") to a class. What can you no longer do?
- Assign attributes not listed in __slots__
- Subclass the class
- Use @property on the class
- Call methods on instances
Answer: Assign attributes not listed in __slots__. With __slots__ there is no per-instance __dict__, so assigning any attribute not declared in __slots__ raises AttributeError. That missing dict is exactly what shrinks the instances.
Inside a closure, which keyword lets an inner function rebind a variable in the enclosing function?
- global
- static
- enclosing
- nonlocal
Answer: nonlocal. nonlocal rebinds the nearest enclosing function scope. global would reach all the way to module level instead, skipping the enclosing scope.
What does writing raise NewError(...) from original do that raise NewError(...) alone does not?
- It suppresses the original traceback
- It sets __cause__, explicitly chaining the exceptions
- It re-raises the original exception
- It converts the error into a warning
Answer: It sets __cause__, explicitly chaining the exceptions. The from clause sets __cause__ and marks the new exception as a direct cause of the original, producing a clear chained traceback.
In the LEGB rule, what does the E stand for?
- Enclosing
- Exception
- External
- Environment
Answer: Enclosing. LEGB is Local, Enclosing, Global, Built-in — the order Python searches for a name. Enclosing covers names in an outer function.
Why should datetimes be stored in UTC and converted to a local zone only for display?
- UTC strings are shorter
- Local zones cannot be stored in a database
- UTC has no daylight saving and never shifts, giving an unambiguous baseline
- zoneinfo only works with UTC
Answer: UTC has no daylight saving and never shifts, giving an unambiguous baseline. UTC never observes DST and never shifts, so it is an unambiguous baseline. Converting with astimezone only at display time avoids DST bugs.
A teammate wants to pickle.loads() a blob downloaded from a public URL. What is the danger?
- Pickle is slower than JSON
- Unpickling untrusted data can execute arbitrary code
- Pickle cannot serialize dictionaries
- The blob would be too large
Answer: Unpickling untrusted data can execute arbitrary code. Unpickling untrusted data can run arbitrary code — a serious security hole. For external data use json instead; reserve pickle for trusted, Python-to-Python state.
Which configparser method reads an integer value and converts it to an int automatically?
- cfg.read_int("app", "workers")
- cfg.get("app", "workers", type=int)
- cfg.parse_int("app", "workers")
- cfg.getint("app", "workers")
Answer: cfg.getint("app", "workers"). ConfigParser exposes typed getters like getint, getboolean, and getfloat that read the raw string and convert it for you.
What does the @property decorator let you do?
- Make a method run in a background thread
- Expose a computed or validated value behind clean obj.x attribute syntax
- Mark a method as abstract
- Cache a function across calls automatically
Answer: Expose a computed or validated value behind clean obj.x attribute syntax. A property exposes a method behind attribute syntax; the setter can validate values, and cached_property memoizes the result.
In the build challenge, why must __init__ assign self.port = port rather than self._port = port?
- _port is a reserved name
- self._port would raise AttributeError
- Assigning through self.port runs the validating property setter
- Both lines behave identically
Answer: Assigning through self.port runs the validating property setter. Assigning through the property name self.port routes the value through the setter so validation runs. Writing self._port directly skips the check.