SmartView · Python | Изучение Python
130 subscribers
373 photos
17 links
📖 Обучение языку программирования Python с самого начала: кратко, интересно и понятно!

📚 Мы стараемся преподавать самое важное в Python, чтобы вас не нагружать.

⚡️ Boost канала: https://t.me/boost/SV_Python

🍀 Желаем приятного изучения!
Download Telegram
Переопределение методов

Изменение поведения унаследованного метода:
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 - сложение трех чисел
👍51👌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())
# Вывод:
# Машина едет по дороге
# Лодка плывет по воде
# Самолет летит по воздуху
🔥31👍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! 🎉
5🔥2👍1👏1
Инициализация 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} лет")
👍41🔥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
Наследование vs Композиция

Когда что использовать?
Наследование (is-a):
· Используйте, когда один класс ЯВЛЯЕТСЯ специализацией другого
· "Собака является Животным"
· "Менеджер является Сотрудником"

Композиция (has-a):
· Используйте, когда один класс ИМЕЕТ другой класс как часть
· "Машина имеет Двигатель"
· "Дом имеет Комнату"

# Наследование там, где нужна композиция
class Car(Engine): # Машина НЕ "является" двигателем!
pass

# Композиция
class Car:
def __init__(self):
self.engine = Engine() # Машина "имеет" двигатель

# Композиция там, где нужно наследование
class Animal:
pass

class Dog:
def __init__(self):
self.animal = Animal() # Собака НЕ "имеет" животное!

# Наследование
class Dog(Animal): # Собака "является" животным
pass
👍3
Что такое декораторы классов?

Декораторы классов — это функции, которые принимают класс и возвращают модифицированный класс. Они позволяют изменять поведение класса без наследования, добавляя новую функциональность или изменяя существующую.

Как работают декораторы классов?
Декоратор класса получает исходный класс, может изменить его атрибуты, добавить новые методы или изменить существующие, и возвращает модифицированный класс.

def add_method(cls):
def greet(self):
return f"Привет, я {self.__class__.__name__}"
cls.greet = greet
return cls

@add_method
class Person:
pass

person = Person()
print(person.greet()) # Привет, я Person
👍3