Ось фінальна версія статті українською, з інтегрованим кодом у кінці.
Класи в Python, ООП та контекстні менеджери: повний посібник
Цей посібник охоплює об’єктно-орієнтоване програмування (ООП) у Python та контекстні менеджери, включно з практичними прикладами перехоплення stdout/stderr і використанням шаблону Tee для одночасного відображення та збереження виводу.
1. Класи в Python
Класи є центральним механізмом ООП. Клас — це шаблон для створення об’єктів з атрибутами (дані) та методами (функції). Об’єкти, створені на основі класу, називаються екземплярами (instances). Класи допомагають структурувати код, підвищувати повторне використання та спрощувати підтримку.
Структура класу
class ClassName:
def __init__(self, param1, param2):
self.param1 = param1
self.param2 = param2
def method(self):
return f'{self.param1} і {self.param2}'
Ключові моменти:
__init__: конструктор для ініціалізації атрибутів об’єкта.self: посилання на поточний екземпляр.- Атрибути: визначаються через
selfі доступні у всіх методах. - Методи: працюють з даними об’єкта.
Створення об’єктів
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def description(self):
return f'{self.year} {self.make} {self.model}'
my_car = Car('Toyota', 'Corolla', 2020)
print(my_car.description()) # Вивід: 2020 Toyota Corolla
Типи методів
- Методи екземпляра — працюють на об’єктах (
self). - Методи класу — працюють на класі (
cls) через@classmethod. - Статичні методи — не залежать від екземпляра чи класу (
@staticmethod).
Наслідування
class Animal:
def speak(self):
return 'Голос тварини'
class Dog(Animal):
def speak(self):
return 'Гав'
Поліморфізм
class Cat(Animal):
def speak(self):
return 'Мяу'
cat = Cat()
print(cat.speak()) # Вивід: Мяу
- Та сама назва методу може працювати по-різному залежно від типу об’єкта.
Інкапсуляція
class Car:
def __init__(self, make, model):
self._make = make # Захищений атрибут
self._model = model
def get_make(self):
return self._make
def set_make(self, make):
self._make = make
Магічні методи та деструктор
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point({self.x}, {self.y})'
p = Point(3, 4)
print(p) # Вивід: Point(3, 4)
ASCII-діаграма: наслідування та поліморфізм
┌─────────┐
│ Animal │
└─────────┘
│ speak() │
└─────────┘
▲
┌───────┴────────┐
│ │
┌─────────┐ ┌─────────┐
│ Dog │ │ Cat │
└─────────┘ └─────────┘
│ speak() │ │ speak() │
└─────────┘ └─────────┘
2. Контекстні менеджери в Python
Контекстні менеджери дозволяють автоматично виділяти та звільняти ресурси, навіть у разі помилок.
Використання @contextmanager
from contextlib import contextmanager
@contextmanager
def my_context():
print("Вхід у контекст")
try:
yield "Ресурс"
finally:
print("Вихід з контексту")
Використання:
with my_context() as res:
print("У контексті:", res)
Перехоплення stdout/stderr і Tee
Python дозволяє перехоплювати stdout, stderr або обидва, а також одночасно відображати та перехоплювати з допомогою шаблону Tee.
3. Повний приклад: oop_and_context.py
"""
oop_and_context.py
Приклади класів, принципів OOP і контекстних менеджерів у Python.
Містить:
- Визначення класів, наслідування, поліморфізм, інкапсуляцію
- Методи екземпляра, класу та статичні
- Магічні методи та деструктор
- Контекстні менеджери з @contextmanager
- Перехоплення stdout, stderr і обох одночасно
- Шаблон Tee: відображення та перехоплення одночасно
- Контекстний менеджер на основі класу
"""
import sys
import io
from contextlib import contextmanager
# ==========================
# Частина 1: Класи та OOP
# ==========================
class Animal:
"""Базовий клас для тварин."""
def speak(self):
return 'Голос тварини'
class Dog(Animal):
def speak(self):
return 'Гав'
class Cat(Animal):
def speak(self):
return 'Мяу'
class Car:
"""Приклад інкапсуляції."""
def __init__(self, make, model, year=None):
self._make = make
self._model = model
self.year = year
def get_make(self):
return self._make
def set_make(self, make):
self._make = make
def description(self):
if self.year:
return f'{self.year} {self._make} {self._model}'
return f'{self._make} {self._model}'
class Point:
"""Клас з магічними методами."""
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point({self.x}, {self.y})'
# ==========================
# Частина 2: Контекстні менеджери
# ==========================
@contextmanager
def my_context():
print("Вхід у контекст")
try:
yield "Ресурс"
finally:
print("Вихід з контексту")
@contextmanager
def capture_stdout():
old_stdout = sys.stdout
sys.stdout = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stdout = old_stdout
@contextmanager
def capture_stderr():
old_stderr = sys.stderr
sys.stderr = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stderr = old_stderr
@contextmanager
def capture_output():
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = sys.stderr = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
class Tee(io.StringIO):
"""Відображення та перехоплення одночасно."""
def __init__(self, original):
super().__init__()
self.original = original
def write(self, text):
self.original.write(text)
super().write(text)
def flush(self):
self.original.flush()
@contextmanager
def capture_stdout_tee():
old_stdout = sys.stdout
sys.stdout = tee = Tee(old_stdout)
try:
yield tee
finally:
sys.stdout = old_stdout
# ==========================
# Частина 3: Контекстний менеджер на основі класу
# ==========================
class OutputCapture:
"""Контекстний менеджер на основі класу для перехоплення stdout."""
def __init__(self):
self._buffer = io.StringIO()
self._old_stdout = None
def __enter__(self):
self._old_stdout = sys.stdout
sys.stdout = self._buffer
return self._buffer
def __exit__(self, exc_type, exc_val, exc_tb):
sys.stdout = self._old_stdout
return False # передати виключення далі
# ==========================
# Частина 4: Демонстрації
# ==========================
def demo_classes():
print("\n--- Демонстрація класів ---")
dog = Dog()
cat = Cat()
print("Собака каже:", dog.speak())
print("Кіт каже:", cat.speak())
my_car = Car('Toyota', 'Corolla', 2020)
print("Опис авто:", my_car.description())
my_car.set_make('Honda')
print("Оновлений бренд:", my_car.get_make())
point = Point(3, 4)
print("Точка:", point)
def demo_context_managers():
print("\n--- Демонстрація контекстних менеджерів ---")
with my_context() as res:
print("У контексті:", res)
with capture_stdout() as out:
print("stdout перехоплено")
print("Буфер:", out.getvalue().strip())
with capture_stderr() as err:
print("Йде у stdout")
sys.stderr.write("Повідомлення про помилку\n")
print("Помилка:", err.getvalue().strip())
with capture_output() as out_all:
print("stdout у комбінованому перехопленні")
sys.stderr.write("stderr у комбінованому перехопленні\n")
print("Комбінований вивід:", out_all.getvalue().strip())
with capture_stdout_tee() as tee_out:
print("Вивід видимий І перехоплений (Tee)")
print("Буфер Tee:", tee_out.getvalue().strip())
with OutputCapture() as class_capture:
print("Перехоплення за допомогою класового менеджера")
print("Буфер класового менеджера:", class_capture.getvalue().strip())
# ==========================
# Основний запуск
# ==========================
if __name__ == "__main__":
demo_classes()
demo_context_managers()