Python Programming from First Principles: Learn to Code, Think Computationally, and Solve Real-World Problems
PDFEPUB
BOOK 000 · CHAPTER 7 · Interfaces and Behavioral Contracts

Interfaces and Behavioral Contracts

On this pageAn Explicit ContractDuck Typing

An interface states what an object must provide: operations, accepted inputs, results and failure behavior. Callers can depend on that contract rather than one concrete class.

The UML model shows this implementation as an abstract base class, with an italic required operation. Solid hollow-triangle arrows represent the subclasses’ inheritance from it.

PaymentMethod+pay(amount)CreditCardPayment+pay(amount)PayPalPayment+pay(amount)

An Explicit Contract

Python’s abc module provides ABC and the @abstractmethod decorator. A subclass must override unresolved abstract operations before it can be instantiated. The next section develops this mechanism.

from abc import ABC, abstractmethod

class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount):
        pass
Listing 329 — An Explicit Contract

Defines PaymentMethod; no visible output.

class CreditCardPayment(PaymentMethod):
    def pay(self, amount):
        print(f"Charging ${amount} to credit card.")

class PayPalPayment(PaymentMethod):
    def pay(self, amount):
        print(f"Processing PayPal payment of ${amount}.")
Listing 330 — An Explicit Contract

Defines CreditCardPayment, PayPalPayment; no visible output.

The implementations simulate payments by printing; neither contacts a payment service.

methods = [CreditCardPayment(), PayPalPayment()]

for m in methods:
    m.pay(49.99)
Listing 331 — An Explicit Contract

Output

Charging $49.99 to credit card.
Processing PayPal payment of $49.99.

Both classes accept pay(amount). An abstract declaration does not prove that their signatures or business rules are correct.

Duck Typing

Duck-typed code calls the required operation without first checking a concrete type. A missing method or incompatible signature fails at runtime.

The UML model shows the inheritance actually present in the code: PDFReport extends Exportable; Chart is independent. Both supply the operation the caller needs.

Exportable+export()PDFReport+export()Chart+export()
class Exportable:
    def export(self):
        pass  # expected to be overridden
Listing 332 — Duck Typing

Defines Exportable; no visible output.

This placeholder does not enforce an abstract contract.

class PDFReport(Exportable):
    def export(self):
        print("Exporting report as PDF...")

class Chart:
    def export(self):
        print("Exporting chart image...")
Listing 333 — Duck Typing

Defines PDFReport, Chart; no visible output.

items = [PDFReport(), Chart()]

for item in items:
    item.export()   # works as long as the method exists
Listing 334 — Duck Typing

Output

Exporting report as PDF...
Exporting chart image...

These export methods print messages only; they create no PDF or image files.

Try it: rename Chart’s export method. Predict which call fails, then restore the shared operation.

We will now examine abstract classes more closely to see how Python can require subclasses to provide specific operations.