Created
March 25, 2025 22:59
-
-
Save mwufi/6b64a9ff6ca74fc8441b4d32ceccfd6e to your computer and use it in GitHub Desktop.
Demo of how to use contextlib (with dogs!)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
A fun demonstration of Python's contextlib module using a doghouse example. | |
This file shows both class-based and decorator-based approaches to context managers. | |
""" | |
import contextlib | |
from typing import List, Optional | |
class Dog: | |
"""A simple dog class that can bark and wag its tail.""" | |
def __init__(self, name: str): | |
self.name = name | |
print(f"{name} jumps into the doghouse! Woof!") | |
def bark(self) -> None: | |
"""Make the dog bark.""" | |
print(f"{self.name}: Woof! Woof!") | |
def wag_tail(self) -> None: | |
"""Make the dog wag its tail.""" | |
print(f"{self.name} wags tail happily!") | |
class Doghouse: | |
"""A context manager that represents a doghouse where dogs can be added and managed.""" | |
def __init__(self): | |
self.dogs: List[Dog] = [] | |
print("Building a new doghouse...") | |
def __enter__(self): | |
"""Method called when entering the context (with statement).""" | |
print("Doghouse is ready for dogs!") | |
return self | |
def __exit__(self, exc_type, exc_val, exc_tb): | |
"""Method called when exiting the context.""" | |
num_dogs = len(self.dogs) | |
if num_dogs == 0: | |
print("Closing the empty doghouse.") | |
else: | |
print(f"Closing the doghouse with {num_dogs} dog(s).") | |
for dog in self.dogs: | |
print(f"Goodbye, {dog.name}!") | |
def add(self, name: str) -> Dog: | |
"""Add a new dog to the doghouse.""" | |
dog = Dog(name) | |
self.dogs.append(dog) | |
return dog | |
def find(self, name: str) -> Optional[Dog]: | |
"""Find a dog by name.""" | |
for dog in self.dogs: | |
if dog.name == name: | |
return dog | |
return None | |
def all_bark(self) -> None: | |
"""Make all dogs bark.""" | |
print("All dogs are barking!") | |
for dog in self.dogs: | |
dog.bark() | |
# Alternative implementation using the @contextmanager decorator | |
@contextlib.contextmanager | |
def simple_doghouse(): | |
"""A simpler doghouse implementation using the contextmanager decorator.""" | |
print("Building a new doghouse...") | |
dogs = [] | |
class SimpleDoghouse: | |
def add(self, name: str) -> Dog: | |
dog = Dog(name) | |
dogs.append(dog) | |
return dog | |
def all_bark(self) -> None: | |
print("All dogs are barking!") | |
for dog in dogs: | |
dog.bark() | |
try: | |
print("Doghouse is ready for dogs!") | |
yield SimpleDoghouse() | |
finally: | |
num_dogs = len(dogs) | |
if num_dogs == 0: | |
print("Closing the empty doghouse.") | |
else: | |
print(f"Closing the doghouse with {num_dogs} dog(s).") | |
for dog in dogs: | |
print(f"Goodbye, {dog.name}!") | |
# Helper function to make it easier to use | |
def doghouse(): | |
"""Create a new doghouse context manager.""" | |
return Doghouse() | |
if __name__ == "__main__": | |
# Using the class-based context manager | |
with doghouse() as house: | |
sparky = house.add(name="Sparky") | |
sparky.bark() | |
house.add(name="Rex") | |
house.all_bark() | |
print("\n--- Using the decorator-based approach ---\n") | |
# Using the decorator-based context manager | |
with simple_doghouse() as house: | |
house.add(name="Bella") | |
house.add(name="Max") | |
house.all_bark() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment