@abstractmethod and Enforcement Rules
Sveip for å vise menyen
@abstractmethod is the decorator that marks a method as required. It has specific rules about how it interacts with other decorators, what happens when subclasses override it, and what counts as a valid implementation.
The Basic Contract
A method decorated with @abstractmethod must be overridden in every concrete subclass. The ABC itself can provide a body – but the body is never called automatically. It serves as a default implementation that subclasses can explicitly invoke with super():
12345678910111213141516171819202122from abc import ABC, abstractmethod class Validator(ABC): @abstractmethod def validate(self, value): # Default logic subclasses can call via super() if value is None: raise ValueError("Value cannot be None") class RangeValidator(Validator): def __init__(self, min_val, max_val): self.min_val = min_val self.max_val = max_val def validate(self, value): super().validate(value) # Calling the abstract method's body if not (self.min_val <= value <= self.max_val): raise ValueError(f"{value} is outside [{self.min_val}, {self.max_val}]") validator = RangeValidator(0, 100) validator.validate(50) # Passes validator.validate(None) # Raises ValueError from super()
Combining @abstractmethod with Other Decorators
@abstractmethod must always be the innermost decorator – closest to the function definition. Placing it on the outside breaks enforcement:
123456789101112131415161718192021222324252627282930313233343536from abc import ABC, abstractmethod class DataSource(ABC): # Correct: @abstractmethod is innermost @property @abstractmethod def connection_string(self): pass # Correct: @abstractmethod is innermost @classmethod @abstractmethod def from_config(cls, config): pass # Correct: @abstractmethod is innermost @staticmethod @abstractmethod def validate_schema(schema): pass class PostgresSource(DataSource): @property def connection_string(self): return "postgresql://localhost:5432/db" @classmethod def from_config(cls, config): return cls() @staticmethod def validate_schema(schema): return isinstance(schema, dict) source = PostgresSource() print(source.connection_string)
Overriding Does Not Require Calling super()
A concrete implementation simply needs to exist – calling super() is optional:
12345678910111213141516171819202122from abc import ABC, abstractmethod class Serializer(ABC): @abstractmethod def serialize(self, data): pass @abstractmethod def deserialize(self, raw): pass class JsonSerializer(Serializer): def serialize(self, data): import json return json.dumps(data) # No super() call required def deserialize(self, raw): import json return json.loads(raw) serializer = JsonSerializer() print(serializer.serialize({"amount": 4500.0, "currency": "USD"}))
What Does Not Count as an Implementation
Defining a method with only pass or only ... in the subclass does satisfy the requirement – the override exists even if it does nothing:
12345678910111213from abc import ABC, abstractmethod class Notifier(ABC): @abstractmethod def send(self, message): pass class SilentNotifier(Notifier): def send(self, message): pass # Valid – override exists, does nothing notifier = SilentNotifier() # No TypeError notifier.send("alert") # Does nothing, no error
The enforcement is syntactic – Python checks that the name exists in the subclass's __dict__ or its MRO, not that it does meaningful work.
Takk for tilbakemeldingene dine!
Spør AI
Spør AI
Spør om hva du vil, eller prøv ett av de foreslåtte spørsmålene for å starte chatten vår