Skip to content

Instantly share code, notes, and snippets.

@mwufi
Created March 25, 2025 22:59
Show Gist options
  • Save mwufi/6b64a9ff6ca74fc8441b4d32ceccfd6e to your computer and use it in GitHub Desktop.
Save mwufi/6b64a9ff6ca74fc8441b4d32ceccfd6e to your computer and use it in GitHub Desktop.
Demo of how to use contextlib (with dogs!)
"""
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