We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Interfaces with Protocols and ABCs step 4 of 26
Build an ABC with an Abstract Property and Classmethod
Build a small family of text codecs on an ABC. Every codec has a name, a
from_config factory that builds it from a settings dictionary, and an
encode method. The base class supplies label, which uses all three.
codec = RotCodec.from_config({"shift": "2"})
codec.name # "rot2"
codec.label("abcdef") # "rot2:cdefab"
Fix the decorator order first
The starter crashes before any test runs:
AttributeError: attribute '__isabstractmethod__' of 'property' objects is not writable
@abstractmethod marks a function as abstract. @property and
@classmethod then read that mark from the function they wrap. Decorators
apply from the bottom up, so @abstractmethod goes directly above the def:
@property
@abstractmethod
def name(self) -> str: ...
@classmethod
@abstractmethod
def from_config(cls, config: Mapping[str, str]) -> Self: ...
The starter has @abstractmethod on top, so it tries to mark the property
object instead, and a property does not allow that.
What to write
Codec has three abstract members and one ordinary method:
name, a read-only property that returns astr.from_config(cls, config), a classmethod that returnsSelf. You metSelfin the type hints track. Here it means thatRotCodec.from_configreturns aRotCodec.encode(self, text: str) -> str.label(text), which returnsf"{self.name}:{self.encode(text)}".
RotCodec(shift: int = 1) moves the first shift characters to the end:
nameisf"rot{shift}".encodereturns exactlytext[shift:] + text[:shift]. Do not reduce the shift modulo the length. With"abc", a shift of 5 returns"abc", and a shift of -1 returns"cab". Empty text stays empty.from_configreturnscls(int(config.get("shift", "1"))), so a missing"shift"means 1.
The starter already has UpperCodec and PartialCodec. PartialCodec leaves
out name on purpose. Python lets you define the class, but creating an instance raises
TypeError, because an abstract member is still missing.
Then write two functions:
try_build(cls: type) -> strcallscls(). It returns"TypeError"if that raisesTypeError, and"constructed"otherwise. Keep the parameter as plaintype. mypy does not let you pass an abstract class as atype[Codec].solve(kind, config, text)looks upkindin adict[str, type[Codec]]that maps"rot"toRotCodecand"upper"toUpperCodec. It builds the codec withfrom_config(config).
What the driver reports
__abstractmethods__ holds the names of the abstract members that a class has
not defined yet.
| Key | Value | Expected |
|---|---|---|
label |
codec.label(text) |
For example, "upper:" for empty text |
codec_abstracts |
sorted(Codec.__abstractmethods__) |
["encode", "from_config", "name"] |
partial_abstracts |
sorted(PartialCodec.__abstractmethods__) |
["name"] |
partial_build |
try_build(PartialCodec) |
"TypeError" |
complete_build |
try_build(UpperCodec) |
"constructed" |
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.