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

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

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

🍀 Желаем приятного изучения!
Download Telegram
Утиная типизация (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
Декораторы с параметрами

Иногда нам нужно передавать параметры в декоратор, чтобы настроить его поведение. Для этого создаем декоратор, который возвращает другой декоратор.

def repeater(num_of_repeats=1):
# Декоратор для повторения вызовов функции
def outer_decorator(func):
def inner_decorator(*args, **kwargs):
# Повторяем вызов num_of_repeats раз
for _ in range(max(1, num_of_repeats)):
print(func(*args, **kwargs))
return inner_decorator
return outer_decorator
👍3👏1
Метаклассы type

Метаклассы — это "классы классов". Они определяют, как создаются классы. В Python каждый класс создается метаклассом, и по умолчанию это встроенный метакласс type.

Как работает создание классов?
Когда вы пишете:
class MyClass:
x = 10


Python выполняет это так:
MyClass = type('MyClass', (), {'x': 10})


Параметры функции type:
· name — имя класса
· bases — кортеж родительских классов
· attrs — словарь атрибутов класса

# Создаем класс динамически
MyDynamicClass = type('MyDynamicClass', (), {
'value': 42,
'get_value': lambda self: self.value,
'set_value': lambda self, new_value: setattr(self, 'value', new_value)
})

obj = MyDynamicClass()
print(obj.get_value()) # 42
obj.set_value(100)
print(obj.get_value()) # 100
👍3
Создание собственных метаклассов

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

class Meta(type):
def __new__(cls, name, bases, attrs):
attrs['created_by_meta'] = True
return super().__new__(cls, name, bases, attrs)

class MyClass(metaclass=Meta):
pass

print(MyClass.created_by_meta) # True
👍3
Практическое применение метаклассов

1. ORM (Object-Relational Mapping) системы
Метаклассы используются в Django ORM и SQLAlchemy для автоматического создания моделей базы данных на основе классов Python.

2. Веб-фреймворки
Фреймворки вроде Django используют метаклассы для автоматической регистрации моделей в админ-панели.

3. API фреймворки
FastAPI и Django REST Framework используют метаклассы для автоматической генерации документации API.

class PluginMeta(type):
registry = {}

def __new__(cls, name, bases, attrs):
new_class = super().__new__(cls, name, bases, attrs)
if name != 'Plugin':
cls.registry[name] = new_class
return new_class

class Plugin(metaclass=PluginMeta):
pass

class EmailPlugin(Plugin): pass
class SMSPlugin(Plugin): pass

print(PluginMeta.registry) # {'EmailPlugin': ..., 'SMSPlugin': ...}
👍3
Одиночка (Singleton)

Singleton — это порождающий паттерн проектирования, который гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к этому экземпляру.

Зачем нужен Singleton?
Представьте ситуацию, когда у вас должно быть только одно подключение к базе данных, один логгер или один конфигурационный файл на все приложение. Singleton гарантирует, что не будет создано несколько экземпляров таких классов.

Проблема, которую решает Singleton:
Без Singleton разные части программы могут создавать свои собственные экземпляры класса, что приводит к:
· Расходованию лишней памяти
· Конфликтам при работе с ресурсами
· Несогласованному состоянию

class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]

class Database(metaclass=Singleton):
pass

db1 = Database()
db2 = Database()
print(db1 is db2) # True
👍3
Фабрика (Factory)

Фабрика — это порождающий паттерн, который предоставляет интерфейс для создания объектов, но позволяет подклассам изменять тип создаваемых объектов. Фабрика инкапсулирует процесс создания объектов, делая код более гибким и расширяемым.

class Dog: 
def speak(self): return "Гав!"
class Cat:
def speak(self): return "Мяу!"

class AnimalFactory:
@staticmethod
def create_animal(animal_type):
if animal_type == "dog": return Dog()
elif animal_type == "cat": return Cat()

animal = AnimalFactory.create_animal("dog")
print(animal.speak()) # Гав!
👍3
Наблюдатель (Observer)

Наблюдатель — это поведенческий паттерн, который позволяет объектам подписываться на события других объектов и автоматически получать уведомления об их изменении. Это создает механизм подписки для уведомления нескольких объектов об изменениях.

class NewsPublisher:
def __init__(self):
self._subscribers = []

def subscribe(self, subscriber):
self._subscribers.append(subscriber)

def publish_news(self, news):
for subscriber in self._subscribers:
subscriber.update(news)

class Subscriber:
def update(self, news):
print(f"Получена новость: {news}")

publisher = NewsPublisher()
subscriber = Subscriber()
publisher.subscribe(subscriber)
publisher.publish_news("Python 3.12 выпущен!")
👍3