Практикум по классам в Python: от простых до сложных задач

Раздел: 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__ вызывается при каждом создании, что может перезаписывать атрибуты. Решение - добавить флаг, инициализирующийся один раз.

- Python 3 произведение чисел (произведение чисел в python 3)
- напиши условие задачи python (написание условия задачи на python)
- объяснения python задача (объяснение задачи на python)

Расширенные примеры работы с классами

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'>}

Задачи на классы в Python - comments

En
задачи на классы в python (python)