bannerbanner
Python: Курс продвинутого Программирования. Часть вторая
Python: Курс продвинутого Программирования. Часть вторая

Полная версия

Python: Курс продвинутого Программирования. Часть вторая

Язык: Русский
Год издания: 2025
Добавлена:
Настройки чтения
Размер шрифта
Высота строк
Поля
На страницу:
2 из 2

ABC: Базовый класс для создания абстрактных классов.

@abstractmethod: Декоратор для обозначения абстрактных методов.

python

from abc import ABC, abstractmethod

# Абстрактный класс Shape – определяет, что такое «фигура» в контексте нашего приложения

class Shape (ABC):

def __init__ (self, name):

self.name = name

@abstractmethod # Обязательный метод для всех производных классов

def area (self):

pass

@abstractmethod # Обязательный метод для всех производных классов

def perimeter (self):

pass

def describe (self): # Обычный метод, который могут использовать подклассы

print (f"This is a shape named {self.name}.»)

# Попытка создать экземпляр абстрактного класса приведет к ошибке

# try:

# shape = Shape («Generic»)

# except TypeError as e:

# print (e) # Выведет: Can’t instantiate abstract class Shape with abstract methods area, perimeter

# Конкретный подкласс, реализующий абстрактные методы

class Circle (Shape):

def __init__ (self, name, radius):

super ().__init__ (name)

if radius <= 0:

raise ValueError («Radius must be positive.»)

self. radius = radius

def area (self): # Реализация абстрактного метода

return 3.14159 * (self. radius ** 2)

def perimeter (self): # Реализация абстрактного метода

return 2 * 3.14159 * self. radius

# Создаем экземпляр конкретного подкласса

my_circle = Circle («MyCircle», 5)

my_circle.describe () # Вызов унаследованного метода

print (f"Area: {my_circle.area ()}»)

print (f"Perimeter: {my_circle.perimeter ()}»)

Абстракция помогает создать четкий контракт для вашего кода, гарантируя, что все объекты, которые должны вести себя как «фигура», будут иметь методы area () и perimeter ().

2.3 Специальные Методы (Magic Methods / Dunder Methods)

Специальные методы, имена которых начинаются и заканчиваются двумя символами подчеркивания (например, __init__, __str__), позволяют нашим классам взаимодействовать со встроенными функциями и операторами Python. Они называются «dunder» (double underscore) методами.

2.3.1 Строковое представление: __str__ и __repr__

__str__ (self): Определяет, что выводится при использовании print () или str () для объекта. Должно возвращать «пользовательское» строковое представление.

__repr__ (self): Определяет «официальное» строковое представление объекта, которое должно быть однозначным и, по возможности, воспроизводимым (т.е., eval (repr (obj)) == obj). Часто используется для отладки. Если __str__ не определен, print () будет использовать __repr__.

python

class Point:

def __init__ (self, x, y):

self. x = x

self. y = y

def __str__ (self): # Более читаемое представление для пользователя

return f»({self. x}, {self. y})»

def __repr__ (self): # Более техническое представление для разработчика

return f"Point (x= {self. x}, y= {self. y})»

p = Point (10, 20)

print (p) # Использует __str__. Вывод: (10, 20)

print (str (p)) # Использует __str__. Вывод: (10, 20)

print (repr (p)) # Использует __repr__. Вывод: Point (x=10, y=20)

# В интерактивной сессии (REPL) без print () обычно вызывается __repr__

#>>> p

# Point (x=10, y=20)

2.3.2 Перегрузка операторов

Специальные методы позволяют перегружать стандартные операторы Python для работы с вашими объектами.

__add__ (self, other): Для оператора +.

__sub__ (self, other): Для оператора -.

__mul__ (self, other): Для оператора *.

__len__ (self): Для функции len ().

__getitem__ (self, key): Для доступа по индексу [].

__eq__ (self, other): Для оператора ==.

python

class Vector:

def __init__ (self, x, y):

self. x = x

self. y = y

def __str__ (self):

return f»({self. x}, {self. y})»

def __add__ (self, other): # Перегрузка оператора сложения +

if not isinstance (other, Vector): # Проверка типа входных данных

return NotImplemented # Указываем, что операция не поддерживается для данного типа

new_x = self. x + other. x

new_y = self. y + other. y

return Vector (new_x, new_y) # Возвращаем новый объект Vector

def __len__ (self): # Перегрузка len ()

# Можно вернуть, например, количество элементов или что-то другое

# Здесь вернем как бы «размер» вектора, но это скорее пример

return 2 # У нас всегда 2 компоненты

v1 = Vector (2, 3)

v2 = Vector (5, 1)

v3 = v1 + v2 # Используем перегруженный оператор +

print (f»{v1} + {v2} = {v3}») # Вывод: (2, 3) + (5, 1) = (7, 4)

print (f"Length of v1: {len (v1)}») # Используем перегруженный len (). Вывод: Length of v1: 2

# Проверка типа с помощью `isinstance`

if isinstance (v1, Vector):

print («v1 is a Vector object.»)

2.4 Абстрактные базовые классы (ABC) и @abstractmethod (Более подробно)

Мы кратко касались этого в контексте абстракции, но важно понять, как это работает на практике. Абстрактные классы служат для определения общего интерфейса, который должны реализовать все подклассы.

abc. ABC: Делает класс абстрактным.

@abstractmethod: Декоратор, который указывает, что метод является абстрактным и должен быть реализован в подклассах.

python

from abc import ABC, abstractmethod

class Shape (ABC): # Абстрактный класс

def __init__ (self, name):

self.name = name

@abstractmethod

def area (self):

«„„Возвращает площадь фигуры.““»

pass # Отсутствие реализации

@abstractmethod

def perimeter (self):

«„„Возвращает периметр фигуры.““»

pass # Отсутствие реализации

def describe (self): # Неабстрактный метод, который может быть унаследован

print (f"This is a shape named {self.name}.»)

class Square (Shape):

def __init__ (self, name, side):

super ().__init__ (name)

self.side = side

def area (self): # Реализация абстрактного метода

return self.side ** 2

def perimeter (self): # Реализация абстрактного метода

return 4 * self.side

class Rectangle (Shape):

def __init__ (self, name, width, height):

super ().__init__ (name)

self. width = width

self. height = height

def area (self): # Реализация абстрактного метода

return self. width * self. height

def perimeter (self): # Реализация абстрактного метода

return 2 * (self. width + self. height)

# Создаем объекты

my_square = Square («MySquare», 5)

my_rect = Rectangle («MyRectangle», 4, 6)

shapes = [my_square, my_rect]

for shape in shapes:

shape.describe () # Унаследованный метод

print (f» Area: {shape.area ()}»)

print (f» Perimeter: {shape.perimeter ()}»)

print (» -" * 10)

# Пример: Если подкласс не реализует все абстрактные методы

# class Triangle (Shape): # ОШИБКА, если не реализовать area () и perimeter ()

# def __init__ (self, name, base, height):

# super ().__init__ (name)

# self.base = base

# self. height = height

# def area (self): # Реализован только один метод

# return 0.5 * self.base * self. height

#

# try:

# t = Triangle («MyTriangle», 3, 4)

# except TypeError as e:

# print (e) # Can’t instantiate abstract class Triangle with abstract methods perimeter

2.5 Композиция и Агрегация (Composition vs. Aggregation)

Это два способа построения отношений «имеет» (has-a) между классами, отличающиеся от наследования («является» is-a).

Композиция: Одноклассник «состоит из» другого класса. Жизненный цикл зависимого объекта полностью контролируется родительским объектом. Если родительский объект уничтожается, зависимый объект тоже уничтожается.

python

class Engine:

def __init__ (self, horsepower):

self. horsepower = horsepower

print («Engine created.»)

def start (self):

print (f"Engine started with {self. horsepower} HP.»)

def __del__ (self): # Магический метод для финальной очистки, вызывается при удалении объекта

print («Engine destroyed.»)

class Car:

def __init__ (self, brand, model, horsepower):

self.brand = brand

self.model = model

# Car «имеет» Engine как свою часть (композиция)

self. engine = Engine (horsepower)

print (f"Car created: {self.brand} {self.model}»)

def start_car (self):

print (f"Starting the car…»)

self.engine.start ()

def __del__ (self):

print (f"Car {self.brand} {self.model} destroyed.»)

# self. engine будет уничтожен автоматически при уничтожении car

my_car = Car («Ford», «Focus», 150)

my_car.start_car ()

print (» -" * 10)

del my_car # Уничтожаем объект car

# Вывод:

# Engine created.

# Car created: Ford Focus

# Starting the car…

# Engine started with 150 HP.

# – — – — —

# Car Ford Focus destroyed.

# Engine destroyed. (Уничтожен Engine, потому что он был частью Car)

Агрегация: Класс содержит ссылку на объект другого класса, но эти объекты существуют независимо. Жизненный цикл зависимого объекта не связан с жизненным циклом родительского.

python

class Address:

def __init__ (self, street, city):

self.street = street

self.city = city

print (f"Address created: {self.street}, {self.city}»)

def __del__ (self):

print (f"Address destroyed: {self.street}, {self.city}»)

class Person:

def __init__ (self, name, address):

self.name = name

# Person «имеет» Address, но Address может существовать и отдельно (агрегация)

self.address = address

print (f"Person created: {self.name}»)

def __del__ (self):

print (f"Person destroyed: {self.name}»)

# Создаем Address отдельно

addr = Address («123 Main St», «Anytown»)

# Создаем Person, передавая Address

person = Person («Alice», addr)

print (» -" * 10)

print(f"{person.name} lives at {person.address.street}, {person.address.city}»)

# Вывод: Alice lives at 123 Main St, Anytown

# Уничтожаем person

del person

# Вывод:

# Address created: 123 Main St, Anytown

# Person created: Alice

# – — – — —

# Person destroyed: Alice

# Address destroyed: 123 Main St, Anytown (Address все еще существует, т.к. он не был частью person)

2.6 Статические методы (@staticmethod) и методы класса (@classmethod)

@staticmethod:

Не получает неявного первого аргумента (self или cls).

Является просто функцией, логически связанной с классом, но не работающей с состоянием ни класса, ни его экземпляров.

Может вызываться как через класс, так и через экземпляр.

python

class MathHelper:

@staticmethod

def add (a, b):

return a + b

print(MathHelper.add (5, 3)) # Вывод: 8

helper = MathHelper ()

print(helper.add (10, 2)) # Вывод: 12

@classmethod:

Получает неявный первый аргумент cls – ссылку на сам класс.

Чаще всего используется для создания альтернативных конструкторов или для работы с атрибутами класса.

python

class Employee:

raise_percent = 1.10 # Атрибут класса

def __init__ (self, name, salary):

self.name = name

self.salary = salary

@classmethod

def from_string (cls, emp_str): # Альтернативный конструктор

# emp_str = «John-50000»

name, salary_str = emp_str. split (» -»)

salary = int (salary_str)

return cls (name, salary) # cls – это сам класс Employee

@classmethod

def get_raise_amount (cls): # Метод, работающий с атрибутом класса

return cls. raise_percent

emp_data = «Jane-60000»

emp1 = Employee.from_string (emp_data) # Создаем объект через метод класса

print(f"{emp1.name}’s salary: {emp1.salary}») # Вывод: Jane’s salary: 60000

print (f"Raise amount: {Employee.get_raise_amount ()}») # Вывод: Raise amount: 1.1

Резюме главы:

Сегодня мы углубили наше понимание ООП, рассмотрев:

Полиморфизм: Способность объектов разных классов реагировать на один и тот же вызов метода по-разному, включая «утиную типизацию» и переопределение методов.

Абстракцию: Использование абстрактных классов (abc, @abstractmethod) для определения интерфейсов и контрактов.

Специальные Методы: Магические методы (__str__, __repr__, __add__, __len__ и др.) для настройки поведения объектов и перегрузки операторов.

Композицию и Агрегацию: Способы построения отношений «имеет» между классами.

Статические Методы (@staticmethod) и Методы Класса (@classmethod): Различия и сферы применения.

Эти концепции позволяют создавать более гибкий, расширяемый и мощный код.

Глава 3: Продвинутые Приемы работы с Данными – Генераторы и Декораторы

В этой главе мы рассмотрим два мощных механизма Python, которые позволяют писать более эффективный и лаконичный код: генераторы и декораторы.

3.1 Генераторы (Generators)

Генераторы – это особый тип итераторов, которые создаются с помощью функций-генераторов (использующих yield) или выражений-генераторов (аналогичных списковым выражениям, но в круглых скобках). Они позволяют создавать последовательности элементов «на лету», не храня всю последовательность в памяти одновременно. Это делает их идеальными для работы с большими объемами данных.

3.1.1 Функции-генераторы (yield)

Вместо return функция-генератор использует ключевое слово yield для возврата значения. При каждом вызове yield функция приостанавливает свое выполнение, запоминая свое состояние, и возвращает указанное значение. При следующем запросе значения (например, в цикле for) выполнение функции возобновляется с того места, где оно было приостановлено.

python

def count_up_to (n):

«„„Генерирует числа от 0 до n-1.““»

i = 0

while i 

print (f» -> Yielding {i}»)

yield i

i += 1

print (f» -> Resumed at {i}»)

# Создаем объект генератора

counter = count_up_to (5)

print («Starting iteration…»)

# Итерируемся по генератору

for number in counter:

print (f"Received: {number}»)

print («Iteration finished.»)

# Пример вывода:

# Starting iteration…

# -> Yielding 0

# Received: 0

# -> Resumed at 1

# -> Yielding 1

# Received: 1

# -> Resumed at 2

# -> Yielding 2

# Received: 2

Конец ознакомительного фрагмента.

Текст предоставлен ООО «Литрес».

Прочитайте эту книгу целиком, купив полную легальную версию на Литрес.

Безопасно оплатить книгу можно банковской картой Visa, MasterCard, Maestro, со счета мобильного телефона, с платежного терминала, в салоне МТС или Связной, через PayPal, WebMoney, Яндекс.Деньги, QIWI Кошелек, бонусными картами или другим удобным Вам способом.

Конец ознакомительного фрагмента
Купить и скачать всю книгу
На страницу:
2 из 2