Skip to content

Commit

Permalink
Merge pull request #549 from realpython/python-protocol
Browse files Browse the repository at this point in the history
Sample code for the article on Python protocols
  • Loading branch information
brendaweles authored Jul 8, 2024
2 parents cfdaba7 + df5edc3 commit 2c52d4f
Show file tree
Hide file tree
Showing 22 changed files with 509 additions and 0 deletions.
3 changes: 3 additions & 0 deletions python-protocol/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Python Protocols: Leveraging Structural Subtyping

This folder provides the code examples for the Real Python tutorial [Python Protocols: Leveraging Structural Subtyping](https://realpython.com/python-protocol/).
27 changes: 27 additions & 0 deletions python-protocol/adder_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from typing import Protocol


class Adder(Protocol):
def add(self, x, y): ...


class IntAdder:
def add(self, x, y):
return x + y


class FloatAdder:
def add(self, x, y):
return x + y


def add(adder: Adder) -> None:
print(adder.add(2, 3))


add(IntAdder())
add(FloatAdder())


for adder in [IntAdder(), FloatAdder()]:
add(adder)
17 changes: 17 additions & 0 deletions python-protocol/adder_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Protocol


class Adder(Protocol):
def add(self, x: float, y: float) -> float: ...


class IntAdder:
def add(self, x: int, y: int) -> int:
return x + y


def add(adder: Adder) -> None:
print(adder.add(2, 3))


# add(IntAdder())
17 changes: 17 additions & 0 deletions python-protocol/adder_v3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Protocol


class Adder(Protocol):
def add(self, x: int | float, y: int | float) -> int | float: ...


class IntAdder:
def add(self, x: int, y: int) -> int:
return x + y


def add(adder: Adder) -> None:
print(adder.add(2, 3))


# add(IntAdder())
25 changes: 25 additions & 0 deletions python-protocol/adder_v4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import Protocol, TypeVar

T = TypeVar("T", bound=int | float)


class Adder(Protocol[T]):
def add(self, x: T, y: T) -> T: ...


class IntAdder:
def add(self, x: int, y: int) -> int:
return x + y


class FloatAdder:
def add(self, x: float, y: float) -> float:
return x + y


def add(adder: Adder) -> None:
print(adder.add(2, 3))


add(IntAdder())
add(FloatAdder())
23 changes: 23 additions & 0 deletions python-protocol/adder_v5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Protocol


class Adder(Protocol):
def add[T: int | float | str](self, x: T, y: T) -> T: ...


class IntAdder:
def add(self, x: int, y: int) -> int:
return x + y


class FloatAdder:
def add(self, x: float, y: float) -> float:
return x + y


def add(adder: Adder) -> None:
print(adder.add(2, 3))


add(IntAdder())
add(FloatAdder())
29 changes: 29 additions & 0 deletions python-protocol/adder_v6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from typing import Protocol


class Adder(Protocol):
def add[T: int | float | str](self, x: T, y: T) -> T: ...


class IntAdder:
def add(self, x: int, y: int) -> int:
return x + y


class FloatAdder:
def add(self, x: float, y: float) -> float:
return x + y


class StrAdder:
def add(self, x: str, y: str) -> str:
return x + y


def add(adder: Adder) -> None:
print(adder.add(2, 3))


add(IntAdder())
add(FloatAdder())
add(StrAdder())
19 changes: 19 additions & 0 deletions python-protocol/animals_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Animal:
def __init__(self, name):
self.name = name

def eat(self):
print(f"{self.name} is eating.")

def drink(self):
print(f"{self.name} is drinking.")


class Dog(Animal):
def bark(self):
print(f"{self.name} is barking.")


class Cat(Animal):
def meow(self):
print(f"{self.name} is meowing.")
26 changes: 26 additions & 0 deletions python-protocol/animals_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class Dog:
def __init__(self, name):
self.name = name

def eat(self):
print(f"{self.name} is eating.")

def drink(self):
print(f"{self.name} is drinking.")

def make_sound(self):
print(f"{self.name} is barking.")


class Cat:
def __init__(self, name):
self.name = name

def eat(self):
print(f"{self.name} is eating.")

def drink(self):
print(f"{self.name} is drinking.")

def make_sound(self):
print(f"{self.name} is meowing.")
16 changes: 16 additions & 0 deletions python-protocol/birds_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Duck:
def quack(self):
return "The duck is quacking!"


def make_it_quack(duck: Duck) -> str:
return duck.quack()


class Person:
def quack(self):
return "The person is imitating a duck quacking!"


print(make_it_quack(Duck()))
# print(make_it_quack(Person()))
21 changes: 21 additions & 0 deletions python-protocol/birds_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class QuackingThing:
def quack(self):
raise NotImplementedError("Subclasses must implement this method")


class Duck(QuackingThing):
def quack(self):
return "The duck is quacking!"


class Person(QuackingThing):
def quack(self):
return "The person is imitating a duck quacking!"


def make_it_quack(duck: QuackingThing) -> str:
return duck.quack()


print(make_it_quack(Duck()))
print(make_it_quack(Person()))
9 changes: 9 additions & 0 deletions python-protocol/calculations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from typing import Union


def add(a: Union[float, int], b: Union[float, int]) -> float:
return float(a + b)


print(add(2, 4))
# print(add("2", "4"))
53 changes: 53 additions & 0 deletions python-protocol/contents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from typing import Protocol


class ContentCreator(Protocol):
def create_content(self) -> str: ...


class Blogger(ContentCreator, Protocol):
posts: list[str]

def add_post(self, title: str, content: str) -> None: ...


class Vlogger(ContentCreator, Protocol):
videos: list[str]

def add_video(self, title: str, path: str) -> None: ...


class Blog:
def __init__(self):
self.blog_posts = []

def create_content(self) -> str:
return "Creating a post."

def add_post(self, title: str, content: str) -> None:
self.blog_posts.append(f"{title}: {content}")
print(f"Post added: {title}")


class Vlog:
def __init__(self):
self.videos = []

def create_content(self) -> str:
return "Recording a video."

def add_video(self, title: str, path: str) -> None:
self.videos.append(f"{title}: {path}")
print(f"Video added: {title}")


def produce_content(creator: ContentCreator):
print(creator.create_content())


def add_post(blogger: Blogger, title: str, content: str):
blogger.add_post(title, content)


def add_video(vlogger: Vlogger, title: str, path: str):
vlogger.add_video(title, path)
7 changes: 7 additions & 0 deletions python-protocol/even_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
def filter_even_numbers(numbers):
return [number for number in numbers if number % 2 == 0]


print(filter_even_numbers([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
print(filter_even_numbers((1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
print(filter_even_numbers({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))
7 changes: 7 additions & 0 deletions python-protocol/even_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
def filter_even_numbers(numbers: list[int]) -> list[int]:
return [number for number in numbers if number % 2 == 0]


print(filter_even_numbers([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
# print(filter_even_numbers((1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
# print(filter_even_numbers({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))
10 changes: 10 additions & 0 deletions python-protocol/even_v3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from collections.abc import Iterable


def filter_even_numbers(numbers: Iterable[int]) -> list[int]:
return [number for number in numbers if number % 2 == 0]


print(filter_even_numbers([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
print(filter_even_numbers((1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
print(filter_even_numbers({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))
33 changes: 33 additions & 0 deletions python-protocol/linked_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from typing import Optional, Protocol


class LinkedListNode(Protocol):
value: int
next_node: Optional["LinkedListNode"]

def __str__(self) -> str:
return f"{self.value} -> {self.next_node}"


class Node:
def __init__(
self,
value: int,
next_node: Optional["LinkedListNode"] = None,
):
self.value = value
self.next_node = next_node

def __str__(self) -> str:
return f"{self.value} -> {self.next_node}"


def print_linked_list(start_node: LinkedListNode):
print(start_node)


node3 = Node(3)
node2 = Node(2, node3)
node1 = Node(1, node2)

print_linked_list(node1)
24 changes: 24 additions & 0 deletions python-protocol/members.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from abc import abstractmethod
from typing import ClassVar, Protocol


class ProtocolMembersDemo(Protocol):
class_attribute: ClassVar[int]
instance_attribute: str

def instance_method(self, arg: int) -> str: ...

@classmethod
def class_method(cls) -> str: ...

@staticmethod
def static_method(arg: int) -> str: ...

@property
def property_name(self) -> str: ...

@property_name.setter
def property_name(self, value: str) -> None: ...

@abstractmethod
def abstract_method(self) -> str: ...
12 changes: 12 additions & 0 deletions python-protocol/person.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Person:
def __init__(self, name):
self.name = name

def eat(self):
print(f"{self.name} is eating.")

def drink(self):
print(f"{self.name} is drinking.")

def talk(self):
print(f"{self.name} is talking.")
Loading

0 comments on commit 2c52d4f

Please sign in to comment.