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

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

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

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

Контроль при установке значений:

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 - сложение трех чисел
👍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