Concrete Methods in Abstract Classes
Scorri per mostrare il menu
An abstract class is not purely abstract. It can contain fully implemented concrete methods that all subclasses inherit without needing to override. This is what makes ABCs more powerful than simple interfaces – they can provide shared behavior alongside enforced contracts.
Shared Logic in the Base Class
Concrete methods in an ABC capture behavior that is common to all implementations, reducing code duplication:
12345678910111213141516171819202122232425262728293031323334353637383940414243from abc import ABC, abstractmethod # ABC with both abstract and concrete methods class ReportExporter(ABC): @abstractmethod def serialize(self, data): pass @abstractmethod def get_extension(self): pass # Concrete method – same for all subclasses def export(self, data, filename): content = self.serialize(data) full_path = f"{filename}.{self.get_extension()}" print(f"Exporting {len(content)} bytes to {full_path}") return full_path class CsvExporter(ReportExporter): def serialize(self, data): headers = ",".join(data[0].keys()) rows = [",".join(str(value) for value in row.values()) for row in data] return "\n".join([headers] + rows) def get_extension(self): return "csv" class JsonExporter(ReportExporter): def serialize(self, data): import json return json.dumps(data) def get_extension(self): return "json" records = [{"id": "TX-001", "amount": 4500.0}, {"id": "TX-002", "amount": 1200.0}] csv_exporter = CsvExporter() csv_exporter.export(records, "transactions") # Uses shared export() logic json_exporter = JsonExporter() json_exporter.export(records, "transactions")
Both exporters reuse export() without reimplementing it. The abstract methods define the variable parts; the concrete method defines the fixed orchestration.
The Template Method Pattern
This is a formal design pattern – an abstract class defines the skeleton of an algorithm in a concrete method, and delegates the variable steps to abstract methods in subclasses:
123456789101112131415161718192021222324252627282930313233343536373839from abc import ABC, abstractmethod # Template method pattern – algorithm skeleton in the base class class DataPipeline(ABC): # Template method – defines the fixed pipeline steps def run(self): raw_data = self.extract() clean_data = self.transform(raw_data) self.load(clean_data) print(f"Pipeline complete: {len(clean_data)} records processed") @abstractmethod def extract(self): pass @abstractmethod def transform(self, data): pass @abstractmethod def load(self, data): pass class SalesPipeline(DataPipeline): def extract(self): return [ {"region": "North", "revenue": 142500.0}, {"region": "South", "revenue": -500.0}, # Invalid – negative {"region": "East", "revenue": 98000.0}, ] def transform(self, data): return [record for record in data if record["revenue"] > 0] def load(self, data): print(f"Loading {len(data)} records to data warehouse") pipeline = SalesPipeline() pipeline.run() # Calls extract → transform → load in the correct order
Overriding Concrete Methods
Subclasses can override concrete methods if they need different behavior – the ABC does not prevent it:
1234567891011121314151617181920212223242526272829from abc import ABC, abstractmethod class Notifier(ABC): @abstractmethod def send(self, message): pass # Concrete method with default behavior def notify_all(self, recipients, message): for recipient in recipients: self.send(f"To {recipient}: {message}") class EmailNotifier(Notifier): def send(self, message): print(f"EMAIL: {message}") class SmsNotifier(Notifier): def send(self, message): print(f"SMS: {message}") # Overriding the concrete method for SMS-specific behavior def notify_all(self, recipients, message): truncated = message[:160] # SMS character limit super().notify_all(recipients, truncated) recipients = ["alice@example.com", "bob@example.com"] EmailNotifier().notify_all(recipients, "Q3 report is ready.") SmsNotifier().notify_all(recipients, "Q3 report is ready.")
Grazie per i tuoi commenti!
Chieda ad AI
Chieda ad AI
Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione