Сеттеры и геттеры
Контроль при установке значений:
class BankAccount:
def __init__(self, balance):
self.__balance = balance
@property
def balance(self):
return self.__balance
@balance.setter
def balance(self, value):
if value >= 0:
self.__balance = value
else:
print("Баланс не может быть отрицательным!")
account = BankAccount(1000)
account.balance = 1500 # ✅ Работает
account.balance = -500 # ❌ "Баланс не может быть отрицательным!"
👍3🔥1👏1👌1
Базовое наследование классов
Простыми словами:
Создание нового класса на основе существующего. Новый класс получает все свойства и методы родителя.
Пример из жизни:
· Родитель: Животное (есть, спать)
· Ребенок: Собака (все то же + лаять)
· Ребенок: Кошка (все то же + мяукать)
class Animal: # Родительский класс
def __init__(self, name):
self.name = name
def eat(self):
return f"{self.name} ест"
def sleep(self):
return f"{self.name} спит"
class Dog(Animal): # Дочерний класс
def bark(self):
return f"{self.name} гавкает"
class Cat(Animal): # Дочерний класс
def meow(self):
return f"{self.name} мяукает"
# Использование
dog = Dog("Бобик")
print(dog.eat()) # ✅ Унаследовано от Animal
print(dog.bark()) # ✅ Собственный метод
cat = Cat("Мурка")
print(cat.sleep()) # ✅ Унаследовано от Animal
print(cat.meow()) # ✅ Собственный метод
👍4🔥1😎1
Переопределение методов
Изменение поведения унаследованного метода:
class Animal:
def __init__(self, name):
self.name = name
def make_sound(self):
return "Какое-то животное издает звук"
class Dog(Animal):
def make_sound(self): # Переопределяем метод
return "Гав!"
class Cat(Animal):
def make_sound(self): # Переопределяем метод
return "Мяу!"
animals = [Dog("Собака"), Cat("Кошка")]
for animal in animals:
print(animal.make_sound())
# Вывод:
# Гав!
# Мяу!
🔥3👍1🤩1👌1
Ключевое слово super()
Обращение к родительскому классу:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # Вызов конструктора родителя
self.breed = breed # Добавляем новое свойство
dog = Dog("Бобик", 3, "Такса")
print(dog.name) # Бобик (от Animal)
print(dog.age) # 3 (от Animal)
print(dog.breed) # Такса (от Dog)
👍3🔥1👏1😁1
Множественное наследование
Наследование от нескольких классов:
class Flyable:
def fly(self):
return "Лечу!"
class Swimmable:
def swim(self):
return "Плыву!"
class Duck(Flyable, Swimmable): # Наследуем от двух классов
def quack(self):
return "Кря-кря!"
duck = Duck()
print(duck.fly()) # ✅ От Flyable
print(duck.swim()) # ✅ От Swimmable
print(duck.quack()) # ✅ Собственный метод
👍3👏2🔥1😁1
Порядок разрешения методов (MRO)
Python ищет методы в определенном порядке:
class A:
def method(self):
return "Метод из A"
class B(A):
def method(self):
return "Метод из B"
class C(A):
def method(self):
return "Метод из C"
class D(B, C):
pass
print(D.__mro__) # Показывает порядок поиска методов
# (<class '__main__.D'>, <class '__main__.B'>,
# <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
obj = D()
print(obj.method()) # "Метод из B" (ищет в порядке MRO)
👏3👍1🔥1🤩1
Абстрактные базовые классы
Классы, которые нельзя создать напрямую:
from abc import ABC, abstractmethod
class Shape(ABC): # Абстрактный класс
@abstractmethod
def area(self):
pass # Дочерние классы ДОЛЖНЫ реализовать этот метод
@abstractmethod
def perimeter(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self): # ✅ Обязательная реализация
return self.width * self.height
def perimeter(self): # ✅ Обязательная реализация
return 2 * (self.width + self.height)
# shape = Shape() # ❌ Ошибка! Нельзя создать абстрактный класс
rect = Rectangle(5, 3) # ✅ Можно создать
print(rect.area()) # 15
👍3🔥1👏1😁1
Понятие полиморфизма
Полиморфизм — это возможность объектов разных классов выполнять одно и то же действие, но каждый по-своему. Простыми словами: "Одна команда — много разных реализаций".
Жизненная аналогия:
Представьте, что вы говорите разным людям "нарисуйте круг":
· Художник нарисует красками на холсте
· Ребенок нарисует карандашом в тетради
· Программист создаст круг в графическом редакторе
Все выполнили одну команду, но разными способами!
Полиморфизм позволяет писать более гибкий и универсальный код. Мы можем работать с разными объектами одинаковым образом, не зная их точного типа.
# Без полиморфизма - нужно знать тип каждого объекта
def make_sound(animal):
if isinstance(animal, Dog):
return "Гав!"
elif isinstance(animal, Cat):
return "Мяу!"
elif isinstance(animal, Cow):
return "Муу!"
# С полиморфизмом - просто вызываем метод
def make_sound(animal):
return animal.speak() # Каждый объект сам знает, как "говорить"
👍3👏2🔥1👌1
Перегрузка методов
Перегрузка методов — это возможность создавать несколько методов с одинаковым именем, но разными параметрами. В Python настоящей перегрузки нет, но мы можем эмулировать её.
Как это работает в Python:
Python не поддерживает перегрузку методов как в Java или C++, но мы можем использовать значения по умолчанию и проверки типов:
class Calculator:
def add(self, a, b, c=None):
if c is None:
# Вариант для двух чисел
return a + b
else:
# Вариант для трех чисел
return a + b + c
# Использование
calc = Calculator()
print(calc.add(2, 3)) # 5 - сложение двух чисел
print(calc.add(2, 3, 4)) # 9 - сложение трех чисел
👍5✍1👌1
Переопределение методов
Переопределение методов — это когда дочерний класс изменяет поведение метода, унаследованного от родительского класса. Метод имеет то же имя, но другую реализацию.
Простая аналогия:
Представьте класс "Транспортное средство" с методом "двигаться":
· Машина двигается по дороге
· Лодка двигается по воде
· Самолет двигается по воздуху
Все они "двигаются", но по-разному!
class Transport:
def move(self):
return "Транспорт движется"
class Car(Transport):
def move(self): # Переопределяем метод
return "Машина едет по дороге"
class Boat(Transport):
def move(self): # Переопределяем метод
return "Лодка плывет по воде"
class Airplane(Transport):
def move(self): # Переопределяем метод
return "Самолет летит по воздуху"
# Демонстрация полиморфизма
transports = [Car(), Boat(), Airplane()]
for transport in transports:
print(transport.move())
# Вывод:
# Машина едет по дороге
# Лодка плывет по воде
# Самолет летит по воздуху
🔥3⚡1👍1😁1
Утиная типизация (Duck Typing)
Это концепция в Python: "Если что-то ходит как утка и крякает как утка, то это probably утка". В программировании: если объект имеет нужный метод, мы можем его использовать, не проверяя его тип.
Знаменитый принцип:
"Don't check whether it IS-a duck: check whether it QUACKS-like-a duck, WALKS-like-a duck, etc"
class Dog:
def speak(self):
return "Гав!"
class Robot:
def speak(self): # У робота тоже есть метод speak!
return "Бип-бип!"
class Person:
def speak(self): # У человека тоже есть метод speak!
return "Привет!"
def make_it_speak(thing):
# Нам не важно, что это за объект
# Важно, что у него есть метод speak()
return thing.speak()
# Все эти объекты могут "говорить"
print(make_it_speak(Dog())) # Гав!
print(make_it_speak(Robot())) # Бип-бип!
print(make_it_speak(Person())) # Привет!
👏4👍2🔥1👌1
Полиморфизм встроенных функций
Встроенный полиморфизм в Python:
Многие встроенные функции Python уже полиморфны — они работают с разными типами данных.
Функция len() — классический пример:
# len() работает с разными типами данных
text = "Привет" # Строка
numbers = [1, 2, 3, 4, 5] # Список
data = {"a": 1, "b": 2} # Словарь
print(len(text)) # 6 (символов в строке)
print(len(numbers)) # 5 (элементов в списке)
print(len(data)) # 2 (пар в словаре)
It's my birthday! 🎉
Инициализация init и new
Магические методы (dunder methods - double underscore) - это специальные методы в Python, которые начинаются и заканчиваются двойным подчеркиванием. Они позволяют настраивать поведение объектов.
init vs new - в чем разница?
· new - создает новый объект (конструктор)
· init - инициализирует уже созданный объект (инициализатор)
Простая аналогия:
Представьте строительство дома:
· new - заливка фундамента и возведение стен (создание)
· init - покраска, расстановка мебели (настройка)
class Person:
def __new__(cls, name, age):
print("1. __new__: Создаем новый объект")
# Создаем объект с помощью родительского new
instance = super().__new__(cls)
return instance
def __init__(self, name, age):
print("2. init: Настраиваем созданный объект")
self.name = name
self.age = age
# Создаем объект
person = Person("Анна", 25)
print(f"3. Результат: {person.name}, {person.age} лет")
👍4⚡1🔥1👨💻1
Строковое представление str и repr
Для чего нужны эти методы?
· str - для "красивого" представления объекта (для пользователей)
· repr - для технического представления (для разработчиков)
Когда они вызываются?
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return f"Книга '{self.title}' - {self.author}"
def __repr__(self):
return f"Book(title='{self.title}', author='{self.author}')"
book = Book("Война и мир", "Л.Н. Толстой")
print(str(book)) # Книга 'Война и мир' - Л.Н. Толстой
print(repr(book)) # Book(title='Война и мир', author='Л.Н. Толстой')
print(book) # Автоматически вызывается __str__
Правило хорошего тона: str и repr должен возвращать строку, которую можно использовать для создания точно такого же объекта.
👍4🔥1😁1
Математические операции add, sub
Магические методы позволяют определить, как объекты будут вести себя в математических операциях.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""Перегрузка оператора + """
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
"""Перегрузка оператора - """
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
"""Перегрузка оператора * (умножение на число)"""
return Vector(self.x * scalar, self.y * scalar)
def __str__(self):
return f"Vector({self.x}, {self.y})"
# Использование
v1 = Vector(2, 3)
v2 = Vector(1, 1)
print(v1 + v2) # Vector(3, 4)
print(v1 - v2) # Vector(1, 2)
print(v1 * 3) # Vector(6, 9)
✍3👍2👌1
Сравнение объектов eq, lt
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __eq__(self, other): # ==
return self.grade == other.grade
def __ne__(self, other): # !=
return self.grade != other.grade
def __lt__(self, other): # <
return self.grade < other.grade
def __le__(self, other): # <=
return self.grade <= other.grade
def __gt__(self, other): # >
return self.grade > other.grade
def __ge__(self, other): # >=
return self.grade >= other.grade
def __str__(self):
return f"{self.name}: {self.grade}"
# Создаем студентов
alice = Student("Алиса", 5)
bob = Student("Боб", 4)
carol = Student("Кэрол", 5)
print(alice == carol) # True (5 == 5)
print(alice != bob) # True (5 != 4)
print(alice > bob) # True (5 > 4)
print(bob < carol) # True (4 < 5)
print(alice >= carol) # True (5 >= 5)
👍3👏1
Работа с коллекциями len, getitem
Делаем наш класс похожим на коллекцию:
class Playlist:
def __init__(self, songs):
self.songs = songs
def __len__(self):
"""Возвращает количество песен в плейлисте"""
return len(self.songs)
def __getitem__(self, index):
"""Возвращает песню по индексу"""
return self.songs[index]
def __contains__(self, song):
"""Проверяет, есть ли песня в плейлисте"""
return song in self.songs
def __str__(self):
return f"Плейлист ({len(self)} песен)"
# Создаем плейлист
my_playlist = Playlist(["Shape of You", "Blinding Lights", "Dance Monkey"])
# Теперь наш класс ведет себя как коллекция
print(len(my_playlist)) # 3
print(my_playlist[1]) # Blinding Lights
print("Shape of You" in my_playlist) # True
# Можно итерироваться
for song in my_playlist:
print(f"▶ {song}")
# Можно использовать срезы
print(my_playlist[0:2]) # ['Shape of You', 'Blinding Lights']
👍3👌1
Вызов объектов call
Делаем объекты вызываемыми как функции:
Метод call позволяет вызывать объекты класса так, как если бы они были функциями.
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, number):
"""Вызывается когда объект используют как функцию"""
return number * self.factor
# Создаем "умножители"
double = Multiplier(2)
triple = Multiplier(3)
quadruple = Multiplier(4)
# Используем как функции
print(double(5)) # 10 (5 * 2)
print(triple(5)) # 15 (5 * 3)
print(quadruple(5)) # 20 (5 * 4)
👍4
Отношения между классами
В объектно-ориентированном программировании классы редко существуют изолированно. Они взаимодействуют друг с другом через различные типы отношений. Два основных типа отношений — это композиция и агрегация.
Простая аналогия:
Представьте отношения между разными объектами в реальном мире:
· Композиция: Дом и комната (комната не существует без дома)
· Агрегация: Университет и студент (студент существует без университета)
Типы отношений:
1. Наследование (is-a) - "является"
2. Композиция (has-a) - "имеет часть" (сильная связь)
3. Агрегация (uses-a) - "использует" (слабая связь)
# Наследование - Car IS-A Vehicle
class Vehicle:
pass
class Car(Vehicle): # Наследование
pass
# Композиция - Car HAS-A Engine
class Engine:
pass
class Car:
def __init__(self):
self.engine = Engine() # Композиция
# Агрегация - University USES Students
class Student:
pass
class University:
def __init__(self):
self.students = [] # Агрегация
👍2👏1
Композиция (сильная связь)
Композиция — это отношение "часть-целое", где часть не может существовать без целого. Если уничтожается основной объект, то уничтожаются и все его части.
Ключевые характеристики:
· Часть создается внутри целого
· Часть не существует отдельно от целого
· Уничтожение целого → уничтожение частей
class Heart:
def __init__(self, beats_per_minute):
self.beats_per_minute = beats_per_minute
def beat(self):
return f"Сердце бьется {self.beats_per_minute} раз в минуту"
class Human:
def __init__(self, name, bpm=70):
self.name = name
self.heart = Heart(bpm) # КОМПОЗИЦИЯ: сердце создается внутри человека
def live(self):
return f"{self.name}: {self.heart.beat()}"
# Использование
person = Human("Иван", 75)
print(person.live()) # Иван: Сердце бьется 75 раз в минуту
# Сердце не существует отдельно от человека
# print(person.heart) # Существует только в контексте человека
👍4
Агрегация (слабая связь)
Объект-часть существует отдельно от объекта-целого:
class Teacher:
def __init__(self, name):
self.name = name
class School:
def __init__(self):
self.teachers = [] # АГРЕГАЦИЯ
def hire_teacher(self, teacher):
self.teachers.append(teacher)
# Учителя создаются ОТДЕЛЬНО от школы
math_teacher = Teacher("Анна")
school = School()
school.hire_teacher(math_teacher)
# Учитель существует без школы
print(math_teacher.name) # Анна
Ключевые характеристики:
· Часть создается ВНЕ целого
· Часть может существовать ОТДЕЛЬНО
· Уничтожение целого НЕ уничтожает части
👍3👏1