Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions python-interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Implementing Interfaces in Python: ABCs and Protocols

This folder provides the code examples for the Real Python tutorial [Implementing Interfaces in Python: ABCs and Protocols](https://realpython.com/python-interface/)
11 changes: 11 additions & 0 deletions python-interface/check_protocols.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from readers_protocols import FileReaderProtocol, PdfReader, EmailReader


def read(reader: FileReaderProtocol, path: str) -> str:
reader.load_file(path)
return reader.extract_text()


# Accepted by the type checker
read(PdfReader(), "/reports/report.pdf")
read(EmailReader(), "/mail/message.eml")
5 changes: 5 additions & 0 deletions python-interface/check_virtual_classes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from meta_classes import EmailReader, PdfReader, VirtualFileReader

print(isinstance(PdfReader(), VirtualFileReader))
print(issubclass(PdfReader, VirtualFileReader))
print(issubclass(EmailReader, VirtualFileReader))
7 changes: 7 additions & 0 deletions python-interface/create_readers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from readers_abc import EmailReader, FileReaderInterface, PdfReader

pdf_reader = PdfReader()
email_reader = EmailReader()

print(isinstance(PdfReader(), FileReaderInterface))
print(issubclass(PdfReader, FileReaderInterface))
37 changes: 37 additions & 0 deletions python-interface/meta_classes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
class ReaderMeta(type):
"""A Reader metaclass used for reader class creation."""

def __instancecheck__(cls, instance):
return cls.__subclasscheck__(type(instance))

def __subclasscheck__(cls, subclass):
return (
hasattr(subclass, "load_file")
and callable(subclass.load_file)
and hasattr(subclass, "extract_text")
and callable(subclass.extract_text)
)


class VirtualFileReader(metaclass=ReaderMeta):
"""Virtual class."""


class PdfReader:
"""Extract text from a PDF."""

def load_file(self, path: str) -> None:
print(f"Loading PDF from {path}")

def extract_text(self) -> str:
return "Extracted PDF text"


class EmailReader:
"""Extract text from an Email."""

def load_file(self, path: str) -> None:
print(f"Loading Email from {path}")

def extract_email_text(self) -> str:
return "Extracted Email text"
36 changes: 36 additions & 0 deletions python-interface/readers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class PdfReader:
"""Extract text from a PDF."""

def load_file(self, path: str) -> None:
print(f"Loading PDF from {path}")

def extract_text(self) -> str:
return "Extracted PDF text"


class EmailReader:
"""Extract text from an Email."""

def load_file(self, path: str) -> None:
print(f"Loading Email from {path}")

def extract_text(self) -> str:
return "Extracted Email text"


def read(reader, path: str) -> str:
reader.load_file(path)
return reader.extract_text()


print(read(PdfReader(), "/reports/report.pdf"))
print(read(EmailReader(), "/mail/message.eml"))

# class EmailReader:
# """Extract text from an Email."""

# def load_file(self, path: str) -> None:
# print(f"Loading Email from {path}")

# def extract_email_text(self) -> str:
# return "Extracted Email text"
37 changes: 37 additions & 0 deletions python-interface/readers_abc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from abc import ABC, abstractmethod


class FileReaderInterface(ABC):
"""Interface for file readers."""

@abstractmethod
def load_file(self, path: str) -> None:
"""Load a file for text extraction."""

@abstractmethod
def extract_text(self) -> str:
"""Return text extracted from the loaded file."""


class PdfReader(FileReaderInterface):
"""Extract text from a PDF."""

def load_file(self, path: str) -> None:
"""Load a PDF file for text extraction."""
print(f"Loading PDF from {path}")

def extract_text(self) -> str:
"""Return text extracted from the loaded PDF."""
return "Extracted PDF text"


class EmailReader(FileReaderInterface):
"""Extract text from an Email."""

def load_file(self, path: str) -> None:
"""Load an EML file for text extraction."""
print(f"Loading Email from {path}")

def extract_email_text(self) -> str:
"""Return text extracted from the loaded Email."""
return "Extracted Email text"
38 changes: 38 additions & 0 deletions python-interface/readers_protocol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from typing import Protocol


# @runtime_checkable # Uncomment for runtime checks of protocol adherence
class FileReaderProtocol(Protocol):
"""Protocol for file readers."""

def load_file(self, path: str) -> None:
"""Load a file for text extraction."""
...

def extract_text(self) -> str:
"""Return text extracted from the loaded file."""
...


class PdfReader:
"""Extract text from a PDF."""

def load_file(self, path: str) -> None:
"""Load a PDF file for text extraction."""
print("Loading your PDF...")

def extract_text(self) -> str:
"""Return text extracted from the loaded PDF."""
return "Your PDF content"


class EmailReader:
"""Extract text from an Email."""

def load_file(self, path: str) -> None:
"""Load an EML file for text extraction."""
print("Loading your Email...")

def extract_text(self) -> str:
"""Return text extracted from the loaded Email."""
return "Your Email content"
12 changes: 12 additions & 0 deletions python-interface/registered_subclass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from virtual_subclass import Double

print(issubclass(float, Double))
print(isinstance(1.2345, Double))


@Double.register
class Double64:
"""A 64-bit double-precision floating-point number."""


print(issubclass(Double64, Double))
8 changes: 8 additions & 0 deletions python-interface/virtual_subclass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from abc import ABC


class Double(ABC):
"""Double precision floating point number."""


Double.register(float)
Loading