Практикум по классам в Python: от простых до сложных задач
Основные подходы к решению задач с классами
Как спроектировать класс для хранения данных и методов работы с ними?
Рассмотрим создание класса BankAccount, который моделирует банковский счет. Основная цель - инкапсулировать баланс и предоставить методы для его изменения.
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.__balance = balance # приватный атрибут
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return True
return False
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
return True
return False
def get_balance(self):
return self.__balance
алгоритм решения задачи python (алгоритм решения задачи на python)
# Пример использования
acc = BankAccount("Иван", 1000)
acc.deposit(500)
print(acc.get_balance()) # 1500
базовые задачи python (базовые задачи python)
Типичная проблема:
Попытка напрямую обратиться к __balance извне приведет к ошибке AttributeError из-за name mangling. Решение: всегда использовать методы доступа или свойство property.
Как реализовать инкапсуляцию с помощью property?
Использование декоратора @property позволяет управлять доступом к атрибутам без явного вызова геттеров и сеттеров.
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = balance # защищенный атрибут
@property
def balance(self):
return self._balance
@balance.setter
def balance(self, value):
if value < 0:
raise ValueError("Баланс не может быть отрицательным")
self._balance = value
задачи для обучения python (задачи для обучения python)
acc = BankAccount("Петр", 500)
print(acc.balance) # 500
acc.balance = 1000 # работает
# acc.balance = -100 # вызовет ValueError
задачи на классы в python (задачи на классы в python)
Ошибка: если сеттер не определен, свойство будет read-only. Также можно забыть про валидацию.
Как реализовать поддержку арифметических операций для объектов пользовательского класса?
Переопределите специальные методы __add__, __sub__ и другие.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
return NotImplemented
def __repr__(self):
return f"Vector({self.x}, {self.y})"
множество python задачи (задачи на множества в python)
v1 = Vector(1, 2) v2 = Vector(3, 4) v3 = v1 + v2 print(v3) # Vector(4, 6)
задачи на модули python (задачи на модули в python)
Если метод возвращает NotImplemented, Python пытается вызвать обратный метод (__radd__) у другого операнда. Если не реализовать, будет TypeError.
Как правильно организовать наследование классов?
Создайте базовый класс Shape и производные классы Circle, Rectangle.
class Shape:
def area(self):
raise NotImplementedError
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
import math
return math.pi * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
задачи на операторы в python (задачи на операторы в python)
shapes = [Circle(5), Rectangle(3, 4)]
for s in shapes:
print(s.area()) # 78.5398..., 12
задачи на последовательности python (задачи на последовательности в python)
Забыть вызвать super().__init__() в наследниках, если базовый класс имеет собственную инициализацию. В нашем примере это не нужно, но в общем случае - обязательно.
Как сделать класс, ведущий себя как контейнер (список, словарь)?
Реализуйте методы __getitem__, __setitem__, __len__.
class MyList:
def __init__(self, items=None):
self._items = list(items) if items else []
def __getitem__(self, index):
return self._items[index]
def __setitem__(self, index, value):
self._items[index] = value
def __len__(self):
return len(self._items)
def append(self, item):
self._items.append(item)
задачи на списки python (задачи на списки в python)
m = MyList([1, 2, 3]) print(m[1]) # 2 m[1] = 10 print(len(m)) # 3
пробелы python задача (задача на пробелы в строке python)
Необходимо обрабатывать некорректные индексы (перехватывать IndexError) и проверять типы. Без __len__ не будет работать функция len().
Как создать альтернативные конструкторы с помощью classmethod?
Декоратор @classmethod позволяет определить методы, которые вызываются от класса и возвращают экземпляр.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def from_birth_year(cls, name, birth_year):
from datetime import datetime
age = datetime.now().year - birth_year
return cls(name, age)
задачи на if else python (задачи на условные операторы if-else в python)
person = Person.from_birth_year("Анна", 1990)
print(person.name, person.age) # Анна, 34 (если 2024)
задачи на работу с файлами python (задачи на работу с файлами в python)
Первый аргумент classmethod - это класс (cls), а не экземпляр. Важно не путать с staticmethod, который не принимает ни cls, ни self.
Как реализовать паттерн Singleton через класс?
Переопределите __new__, чтобы всегда возвращать один и тот же экземпляр.
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, value):
self.value = value
задачи на функции в python (задачи на функции в python)
a = Singleton(10) b = Singleton(20) print(a is b) # True print(a.value, b.value) # 20 20 (потому что __init__ выполняется каждый раз)
Проблема: __init__ вызывается при каждом создании, что может перезаписывать атрибуты. Решение - добавить флаг, инициализирующийся один раз.
Расширенные примеры работы с классами
1. Класс для комплексных чисел с полной арифметикой
Реализуем класс Complex, поддерживающий сложение, вычитание, умножение и сравнение.
class Complex:
def __init__(self, real, imag=0):
self.real = real
self.imag = imag
def __add__(self, other):
if isinstance(other, Complex):
return Complex(self.real + other.real, self.imag + other.imag)
return NotImplemented
def __sub__(self, other):
if isinstance(other, Complex):
return Complex(self.real - other.real, self.imag - other.imag)
return NotImplemented
def __mul__(self, other):
if isinstance(other, Complex):
real = self.real * other.real - self.imag * other.imag
imag = self.real * other.imag + self.imag * other.real
return Complex(real, imag)
return NotImplemented
def __repr__(self):
sign = '+' if self.imag >= 0 else '-'
return f"{self.real} {sign} {abs(self.imag)}i"
def __eq__(self, other):
if isinstance(other, Complex):
return self.real == other.real and self.imag == other.imag
return NotImplemented
c1 = Complex(3, 4) c2 = Complex(1, -2) print(c1 + c2) # 4 + 2i print(c1 - c2) # 2 + 6i print(c1 * c2) # 11 - 2i print(c1 == Complex(3, 4)) # True
2. Класс для представления ориентированного графа с методами добавления ребер и обходом
Используются словари для хранения списков смежности.
class Graph:
def __init__(self):
self.adj = {}
def add_vertex(self, vertex):
if vertex not in self.adj:
self.adj[vertex] = []
def add_edge(self, v1, v2):
self.add_vertex(v1)
self.add_vertex(v2)
self.adj[v1].append(v2)
def dfs(self, start, visited=None):
if visited is None:
visited = set()
visited.add(start)
print(start, end=' ')
for neighbor in self.adj.get(start, []):
if neighbor not in visited:
self.dfs(neighbor, visited)
return visited
g = Graph()
g.add_edge('A', 'B')
g.add_edge('A', 'C')
g.add_edge('B', 'D')
g.add_edge('C', 'D')
print('DFS:', end=' ')
g.dfs('A') # A B D C
3. Использование __slots__ для экономии памяти
Класс с фиксированным набором атрибутов, что уменьшает потребление памяти.
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
# Попытка добавить новый атрибут вызовет AttributeError
p = Point(1, 2)
# p.z = 3 # Ошибка
print(p.x, p.y) # 1 2
print(p.__slots__) # ('x', 'y')
4. Дескрипторы для управления атрибутами
Создаем дескриптор, который проверяет, что значение является положительным числом.
class PositiveNumber:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
if not isinstance(value, (int, float)) or value <= 0:
raise ValueError(f"{self.name} должно быть положительным числом")
instance.__dict__[self.name] = value
class Order:
quantity = PositiveNumber()
price = PositiveNumber()
def __init__(self, quantity, price):
self.quantity = quantity
self.price = price
o = Order(5, 100.0) print(o.quantity, o.price) # 5 100.0 # o.quantity = -1 # ValueError
5. Метакласс для автоматической регистрации классов
Метакласс добавляет каждый созданный класс в реестр.
registry = {}
class RegistryMeta(type):
def __new__(cls, name, bases, namespace):
new_class = super().__new__(cls, name, bases, namespace)
registry[name] = new_class
return new_class
class Base(metaclass=RegistryMeta):
pass
class Dog(Base):
def bark(self):
print("Woof!")
class Cat(Base):
def meow(self):
print("Meow!")
print(registry) # {'Base': <class '__main__.Base'>, 'Dog': <class '__main__.Dog'>, 'Cat': <class '__main__.Cat'>}