🤔 Какая сложность у пузырьковой сортировки?
Пузырьковая сортировка (Bubble Sort) — это один из самых простых, но неэффективных алгоритмов сортировки.
🚩Как работает пузырьковая сортировка?
1. Проходим по массиву несколько раз.
2. На каждой итерации сравниваем соседние элементы и меняем их местами, если они идут не в том порядке.
3. После первого прохода наибольший элемент оказывается в конце массива.
4. Повторяем процесс, пока массив не отсортируется.
🚩Рассчёт сложности (`O(n²)`)
Количество сравнений в худшем случае:
- На первой итерации:
- На второй:
- На третьей:
- …
- Всего:
Количество обменов (swap) в худшем случае:
- Если массив полностью перевёрнут, на каждой итерации будет максимальное количество перестановок →
🚩Оптимизированная пузырьковая сортировка (`O(n)`)
Если на проходе по массиву не было перестановок, значит массив уже отсортирован.
Ставь 👍 и забирай 📚 Базу знаний
Пузырьковая сортировка (Bubble Sort) — это один из самых простых, но неэффективных алгоритмов сортировки.
🚩Как работает пузырьковая сортировка?
1. Проходим по массиву несколько раз.
2. На каждой итерации сравниваем соседние элементы и меняем их местами, если они идут не в том порядке.
3. После первого прохода наибольший элемент оказывается в конце массива.
4. Повторяем процесс, пока массив не отсортируется.
🚩Рассчёт сложности (`O(n²)`)
Количество сравнений в худшем случае:
- На первой итерации:
n-1 сравнений - На второй:
n-2 сравнений - На третьей:
n-3 сравнений - …
- Всего:
(n-1) + (n-2) + ... + 1 = O(n²) Количество обменов (swap) в худшем случае:
- Если массив полностью перевёрнут, на каждой итерации будет максимальное количество перестановок →
O(n²). 🚩Оптимизированная пузырьковая сортировка (`O(n)`)
Если на проходе по массиву не было перестановок, значит массив уже отсортирован.
def bubble_sort(arr):
n = len(arr)
for i in range(n):
swapped = False # Флаг, отслеживающий перестановки
for j in range(n - i - 1):
if arr[j] > arr[j + 1]: # Если элементы в неправильном порядке, меняем местами
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
if not swapped:
break # Если перестановок не было, завершаем сортировку
arr = [1, 2, 3, 4, 5] # Уже отсортированный массив
bubble_sort(arr)
print(arr) # [1, 2, 3, 4, 5]
Ставь 👍 и забирай 📚 Базу знаний
🔥3
🤔 Что такое паттерн Мост (Bridge)?
Паттерн "Мост" (Bridge) является структурным паттерном проектирования, который предназначен для разделения абстракции и реализации так, чтобы они могли изменяться независимо друг от друга. Этот паттерн полезен, когда класс должен работать с различными платформами или когда нужно избежать жесткой связки между абстракцией и ее реализацией.
🚩 Зачем нужен
🟠Разделение абстракции и реализации:
Он позволяет отделить абстракцию от ее реализации, что упрощает поддержку и расширение системы.
🟠Уменьшение количества подклассов:
Без применения этого паттерна, если у нас есть несколько вариантов абстракции и несколько вариантов реализации, то нам пришлось бы создавать классы для всех возможных комбинаций, что приводит к взрывному росту количества классов.
🟠Гибкость:
Это позволяет изменять и абстракцию, и реализацию независимо друг от друга.
🚩Как используется
🟠Абстракция (Abstraction):
Определяет интерфейс и хранит ссылку на объект Implementor.
🟠Расширенная абстракция (RefinedAbstraction):
Наследует Abstraction и расширяет интерфейс.
🟠Реализатор (Implementor):
Определяет интерфейс для всех реализаций.
🟠Конкретный реализатор (ConcreteImplementor):
Реализует интерфейс Implementor.
Допустим, у нас есть программа для управления различными типами устройств (например, телевизор и радио), которые можно включать и выключать. Мы хотим, чтобы способ управления устройствами мог изменяться независимо от типов устройств.
Ставь 👍 и забирай 📚 Базу знаний
Паттерн "Мост" (Bridge) является структурным паттерном проектирования, который предназначен для разделения абстракции и реализации так, чтобы они могли изменяться независимо друг от друга. Этот паттерн полезен, когда класс должен работать с различными платформами или когда нужно избежать жесткой связки между абстракцией и ее реализацией.
🚩 Зачем нужен
🟠Разделение абстракции и реализации:
Он позволяет отделить абстракцию от ее реализации, что упрощает поддержку и расширение системы.
🟠Уменьшение количества подклассов:
Без применения этого паттерна, если у нас есть несколько вариантов абстракции и несколько вариантов реализации, то нам пришлось бы создавать классы для всех возможных комбинаций, что приводит к взрывному росту количества классов.
🟠Гибкость:
Это позволяет изменять и абстракцию, и реализацию независимо друг от друга.
🚩Как используется
🟠Абстракция (Abstraction):
Определяет интерфейс и хранит ссылку на объект Implementor.
🟠Расширенная абстракция (RefinedAbstraction):
Наследует Abstraction и расширяет интерфейс.
🟠Реализатор (Implementor):
Определяет интерфейс для всех реализаций.
🟠Конкретный реализатор (ConcreteImplementor):
Реализует интерфейс Implementor.
Допустим, у нас есть программа для управления различными типами устройств (например, телевизор и радио), которые можно включать и выключать. Мы хотим, чтобы способ управления устройствами мог изменяться независимо от типов устройств.
# Implementor
class Device:
def is_enabled(self):
pass
def enable(self):
pass
def disable(self):
pass
# ConcreteImplementor
class TV(Device):
def __init__(self):
self._on = False
def is_enabled(self):
return self._on
def enable(self):
self._on = True
def disable(self):
self._on = False
class Radio(Device):
def __init__(self):
self._on = False
def is_enabled(self):
return self._on
def enable(self):
self._on = True
def disable(self):
self._on = False
# Abstraction
class RemoteControl:
def __init__(self, device):
self._device = device
def toggle_power(self):
if self._device.is_enabled():
self._device.disable()
else:
self._device.enable()
# RefinedAbstraction
class AdvancedRemoteControl(RemoteControl):
def mute(self):
print("Device is muted.")
# Клиентский код
tv = TV()
remote = RemoteControl(tv)
remote.toggle_power() # Включает TV
radio = Radio()
advanced_remote = AdvancedRemoteControl(radio)
advanced_remote.toggle_power() # Включает Radio
advanced_remote.mute() # Заглушает Radio
Ставь 👍 и забирай 📚 Базу знаний
👍3
🤔 Какие основные функции Python есть?
Встроенные функции Python включают такие базовые инструменты, как len, type, print, input, int, str, list, dict, sum, max, min, zip, enumerate, range, map, filter и многие другие. Они обеспечивают широкий функционал без необходимости импортировать внешние библиотеки.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Встроенные функции Python включают такие базовые инструменты, как len, type, print, input, int, str, list, dict, sum, max, min, zip, enumerate, range, map, filter и многие другие. Они обеспечивают широкий функционал без необходимости импортировать внешние библиотеки.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Зачем нужен volume в docker?
В Docker
🚩Зачем нужен `volume`?
Данные не теряются при удалении контейнера
Общий доступ: несколько контейнеров могут использовать один и тот же
Производительность: тома быстрее, чем хранение внутри контейнера
Разделение кода и данных: удобно для баз данных и логов
🚩Как создать `volume`?
Способ 1: Автоматическое создание при запуске контейнера
Способ 2: Создать том отдельно и подключить его
Способ 3: Использовать путь на хосте (bind-mount)
🚩Как посмотреть список томов?
🚩Как удалить `volume`?
Важно: при удалении тома данные удаляются безвозвратно!
Ставь 👍 и забирай 📚 Базу знаний
В Docker
volume (том) — это специальное место для хранения данных контейнера, которое не исчезает при перезапуске или удалении контейнера. 🚩Зачем нужен `volume`?
Данные не теряются при удалении контейнера
Общий доступ: несколько контейнеров могут использовать один и тот же
volume Производительность: тома быстрее, чем хранение внутри контейнера
Разделение кода и данных: удобно для баз данных и логов
🚩Как создать `volume`?
Способ 1: Автоматическое создание при запуске контейнера
docker run -d -v my_volume:/app/data my_container
Способ 2: Создать том отдельно и подключить его
docker volume create my_volume
docker run -d -v my_volume:/app/data my_container
Способ 3: Использовать путь на хосте (bind-mount)
docker run -d -v /home/user/data:/app/data my_container
🚩Как посмотреть список томов?
docker volume ls # Покажет все тома
docker volume inspect my_volume # Информация о томе
🚩Как удалить `volume`?
Важно: при удалении тома данные удаляются безвозвратно!
docker volume rm my_volume # Удаление одного тома
docker volume prune # Удаление всех неиспользуемых томов
Ставь 👍 и забирай 📚 Базу знаний
👍1
🤔 Что такое dict comprehensions?
🚩Простейший пример `dict comprehension`
Вывод
🚩`dict comprehension` с условием (`if`)
Оставляем только чётные числа:
Вывод
🚩Преобразование списка в словарь
Создаём словарь из списка пар (
Вывод
🚩Обратный словарь (ключи и значения меняются местами)
Инвертируем словарь
Вывод
🚩Генерация словаря из `zip()`
Объединяем два списка в словарь
Вывод
🚩`dict comprehension` с `if-else`
Разделяем числа на чётные и нечётные
Вывод
Ставь 👍 и забирай 📚 Базу знаний
dict comprehension (генератор словаря) — это способ быстро создать словарь с помощью компактного синтаксиса, похожего на list comprehension. 🚩Простейший пример `dict comprehension`
squares = {x: x**2 for x in range(1, 6)}
print(squares)Вывод
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}🚩`dict comprehension` с условием (`if`)
Оставляем только чётные числа:
squares = {x: x**2 for x in range(1, 11) if x % 2 == 0}
print(squares)Вывод
{2: 4, 4: 16, 6: 36, 8: 64, 10: 100}🚩Преобразование списка в словарь
Создаём словарь из списка пар (
name → длина слова) names = ["Alice", "Bob", "Charlie"]
name_lengths = {name: len(name) for name in names}
print(name_lengths)
Вывод
{'Alice': 5, 'Bob': 3, 'Charlie': 7}🚩Обратный словарь (ключи и значения меняются местами)
Инвертируем словарь
{ключ: значение} → {значение: ключ} original = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in original.items()}
print(inverted)Вывод
{1: 'a', 2: 'b', 3: 'c'}🚩Генерация словаря из `zip()`
Объединяем два списка в словарь
keys = ["name", "age", "city"]
values = ["Alice", 25, "New York"]
person = {k: v for k, v in zip(keys, values)}
print(person)
Вывод
{'name': 'Alice', 'age': 25, 'city': 'New York'}🚩`dict comprehension` с `if-else`
Разделяем числа на чётные и нечётные
numbers = range(1, 6)
parity = {x: "чётное" if x % 2 == 0 else "нечётное" for x in numbers}
print(parity)
Вывод
{1: 'нечётное', 2: 'чётное', 3: 'нечётное', 4: 'чётное', 5: 'нечётное'}Ставь 👍 и забирай 📚 Базу знаний
🤔 В чем отличие изменяемые / неизменяемые?
В Python все данные делятся на изменяемые (mutable) и неизменяемые (immutable)
🚩Неизменяемые (`immutable`) типы
После создания их нельзя изменить!
Пример:
🚩Изменяемые (`mutable`) типы
Можно менять их содержимое без создания нового объекта.
Пример:
🚩Почему это важно?
Неизменяемые объекты безопаснее для ключей
Используем
Ошибки с изменяемыми значениями по умолчанию
Используем
🚩Копирование объектов (`copy()` vs `deepcopy()`)
Используем
Ставь 👍 и забирай 📚 Базу знаний
В Python все данные делятся на изменяемые (mutable) и неизменяемые (immutable)
🚩Неизменяемые (`immutable`) типы
После создания их нельзя изменить!
x = 10
print(id(x)) # 140735598722544 (адрес в памяти)
x = x + 5 # Создаётся новый объект!
print(id(x)) # 140735598722704 (новый адрес)
Пример:
str неизменяемая s = "hello"
print(id(s)) # 140735598723664
s = s + " world" # Создаётся новая строка!
print(id(s)) # 140735598724240 (новый адрес)
🚩Изменяемые (`mutable`) типы
Можно менять их содержимое без создания нового объекта.
lst = [1, 2, 3]
print(id(lst)) # 140735598722544
lst.append(4) # Изменяем список
print(id(lst)) # 140735598722544 (адрес остался тот же!)
Пример:
dict изменяемый d = {"name": "Alice"}
print(id(d)) # 140735598723664
d["age"] = 25 # Добавляем ключ
print(id(d)) # 140735598723664 (адрес не изменился!)🚩Почему это важно?
Неизменяемые объекты безопаснее для ключей
dict и set d = {}
d[[1, 2, 3]] = "Ошибка!" # ❌ TypeError: unhashable type: 'list'Используем
tuple вместо list (он неизменяемый) d[(1, 2, 3)] = "OK"
Ошибки с изменяемыми значениями по умолчанию
def add_item(lst=[]): # ❌ Опасный код!
lst.append(1)
return lst
print(add_item()) # [1]
print(add_item()) # [1, 1] ❌ Список не создаётся заново!
Используем
None вместо списка def add_item(lst=None):
if lst is None:
lst = []
lst.append(1)
return lst
🚩Копирование объектов (`copy()` vs `deepcopy()`)
copy() делает поверхностную копию (новый объект, но старые вложенные элементы). deepcopy() делает глубокую копию (всё новое). import copy
lst1 = [[1, 2, 3], [4, 5, 6]]
lst2 = copy.copy(lst1) # Поверхностная копия
lst2[0][0] = 99
print(lst1) # [[99, 2, 3], [4, 5, 6]] ❌ Исходный список изменился!
Используем
deepcopy() для полной независимой копии lst3 = copy.deepcopy(lst1)
lst3[0][0] = 100
print(lst1) # [[99, 2, 3], [4, 5, 6]] ✅ Не изменился!
Ставь 👍 и забирай 📚 Базу знаний
🤔 Можно ли при вызове метода save указать какие поля изменять?
Да, в Django ORM можно указать конкретные поля для сохранения, используя параметр
🚩Как использовать `update_fields`
Пример модели
Обновляем только поле
🚩Что делает `update_fields`?
Генерирует SQL-запрос только для указанных полей, например:
🚩Когда `update_fields` полезен?
Уменьшает нагрузку на БД, так как обновляет только нужные поля.
Полезен, если нужно изменить одно поле, а не всю запись.
Избегает ненужных изменений в
🚩Ограничения `update_fields`
Нельзя использовать при создании объекта (
Не обновляет
Решение: обновить вручную:
Ставь 👍 и забирай 📚 Базу знаний
Да, в Django ORM можно указать конкретные поля для сохранения, используя параметр
update_fields в методе .save(). 🚩Как использовать `update_fields`
Пример модели
from django.db import models
class UserProfile(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
age = models.IntegerField()
Обновляем только поле
name, не трогая email и age user = UserProfile.objects.get(id=1)
user.name = "Новый пользователь"
user.save(update_fields=["name"]) # Обновит только поле `name`
🚩Что делает `update_fields`?
Генерирует SQL-запрос только для указанных полей, например:
UPDATE user_profile SET name = 'Новый пользователь' WHERE id = 1;
🚩Когда `update_fields` полезен?
Уменьшает нагрузку на БД, так как обновляет только нужные поля.
Полезен, если нужно изменить одно поле, а не всю запись.
Избегает ненужных изменений в
auto_now и auto_now_add полях (DateTimeField). 🚩Ограничения `update_fields`
Нельзя использовать при создании объекта (
save() с update_fields не работает для .create()). user = UserProfile(name="Alice", email="alice@example.com")
user.save(update_fields=["name"]) # ❌ Ошибка, объект ещё не в базе!
Не обновляет
auto_now-поля (DateTimeField) автоматически! updated_at = models.DateTimeField(auto_now=True) # Не обновится с `update_fields`
Решение: обновить вручную:
user.updated_at = timezone.now()
user.save(update_fields=["name", "updated_at"])
Ставь 👍 и забирай 📚 Базу знаний
🤔 Что такое звёздный синтаксис (*)?
В Python * используется: – при распаковке (например, аргументов или списков),
– для переменного числа аргументов функции,
– в срезах и других операциях с коллекциями.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
В Python * используется: – при распаковке (например, аргументов или списков),
– для переменного числа аргументов функции,
– в срезах и других операциях с коллекциями.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍1🤔1💊1
🤔 Какие ограничения есть у рекурсии в Python?
Рекурсия — это мощный инструмент, но в Python она имеет ограничения, которые нужно учитывать при написании кода.
🟠Ограничение на глубину рекурсии (`sys.getrecursionlimit()`)
В Python по умолчанию рекурсия ограничена 1000 вызовами, чтобы избежать переполнения стека.
Если превысить этот лимит, программа вызовет ошибку
🚩Как изменить лимит?
Можно увеличить глубину рекурсии, но это небезопасно
🟠Рекурсия требует много памяти
Каждый рекурсивный вызов создаёт новый фрейм в стеке вызовов.
🟠Отсутствие оптимизации хвостовой рекурсии
Другие языки (например, Lisp, JavaScript) автоматически оптимизируют хвостовую рекурсию (Tail Call Optimization, TCO).
Python не делает этого, поэтому даже "идеальная" рекурсия всё равно переполняет стек.
🟠Рекурсия медленнее цикла
Рекурсивный вызов требует больше накладных расходов (создание стек-фреймов), чем обычный
Ставь 👍 и забирай 📚 Базу знаний
Рекурсия — это мощный инструмент, но в Python она имеет ограничения, которые нужно учитывать при написании кода.
🟠Ограничение на глубину рекурсии (`sys.getrecursionlimit()`)
В Python по умолчанию рекурсия ограничена 1000 вызовами, чтобы избежать переполнения стека.
import sys
print(sys.getrecursionlimit()) # 1000 (обычное значение)
Если превысить этот лимит, программа вызовет ошибку
def recursive():
return recursive()
recursive() # RecursionError: maximum recursion depth exceeded
🚩Как изменить лимит?
Можно увеличить глубину рекурсии, но это небезопасно
sys.setrecursionlimit(2000) # Увеличиваем до 2000
🟠Рекурсия требует много памяти
Каждый рекурсивный вызов создаёт новый фрейм в стеке вызовов.
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)
print(factorial(10000)) # Ошибка из-за переполнения стека
🟠Отсутствие оптимизации хвостовой рекурсии
Другие языки (например, Lisp, JavaScript) автоматически оптимизируют хвостовую рекурсию (Tail Call Optimization, TCO).
Python не делает этого, поэтому даже "идеальная" рекурсия всё равно переполняет стек.
def tail_recursive(n, acc=1):
if n == 1:
return acc
return tail_recursive(n - 1, n * acc)
print(tail_recursive(1000)) # Всё равно вызовет RecursionError
🟠Рекурсия медленнее цикла
Рекурсивный вызов требует больше накладных расходов (создание стек-фреймов), чем обычный
for или while.# Итеративный вариант (быстрее)
def factorial_iter(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
# Рекурсивный вариант (медленнее)
def factorial_rec(n):
if n == 1:
return 1
return n * factorial_rec(n - 1)
Ставь 👍 и забирай 📚 Базу знаний
👍1
🤔 Какие есть операторы ограничений?
Ограничения (constraints) в SQL используются для контроля целостности данных в таблицах. Они помогают предотвратить некорректные значения и обеспечить согласованность данных.
🚩`NOT NULL` (Запрещает `NULL`)
Используется, если поле обязательно для заполнения
Нельзя вставить
🚩`UNIQUE` (Гарантирует уникальность значений)
Запрещает дубликаты в столбце
Попытка вставить одинаковый email вызовет ошибку
Создание
🚩`PRIMARY KEY` (Главный ключ, уникальный идентификатор)
Объединяет
Можно создать
🚩`FOREIGN KEY` (Связь таблиц)
Создаёт связь между таблицами и поддерживает ссылочную целостность.
Есть таблица пользователей (
Что делать при удалении пользователя?
Ставь 👍 и забирай 📚 Базу знаний
Ограничения (constraints) в SQL используются для контроля целостности данных в таблицах. Они помогают предотвратить некорректные значения и обеспечить согласованность данных.
🚩`NOT NULL` (Запрещает `NULL`)
Используется, если поле обязательно для заполнения
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL -- Поле name обязательно
);
Нельзя вставить
NULL в name INSERT INTO users (id, name) VALUES (1, NULL); -- Ошибка!
🚩`UNIQUE` (Гарантирует уникальность значений)
Запрещает дубликаты в столбце
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE -- Email должен быть уникальным
);
Попытка вставить одинаковый email вызовет ошибку
INSERT INTO users (id, email) VALUES (1, 'test@example.com');
INSERT INTO users (id, email) VALUES (2, 'test@example.com'); -- Ошибка!
Создание
UNIQUE на нескольких колонках CREATE TABLE orders (
user_id INT,
product_id INT,
UNIQUE (user_id, product_id) -- Запрещает заказывать один товар дважды
);
🚩`PRIMARY KEY` (Главный ключ, уникальный идентификатор)
Объединяет
NOT NULL + UNIQUE и гарантирует, что строка уникальна. CREATE TABLE users (
id INT PRIMARY KEY, -- Уникальный идентификатор
name VARCHAR(50)
);
Можно создать
PRIMARY KEY на нескольких колонках CREATE TABLE enrollments (
student_id INT,
course_id INT,
PRIMARY KEY (student_id, course_id) -- Один студент не может записаться дважды на один курс
);
🚩`FOREIGN KEY` (Связь таблиц)
Создаёт связь между таблицами и поддерживает ссылочную целостность.
Есть таблица пользователей (
users) и таблица заказов (orders), где user_id в orders должен ссылаться на id в users. CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
FOREIGN KEY (user_id) REFERENCES users(id) -- Связь с таблицей users
);
Что делать при удалении пользователя?
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
Ставь 👍 и забирай 📚 Базу знаний
🤔 Как собрать образ в Docker?
Образ собирается из Dockerfile при помощи команды сборки. В процессе выполнения создаются слои, соответствующие каждой инструкции в Dockerfile. По завершении формируется итоговый образ, который можно запускать как контейнер.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Образ собирается из Dockerfile при помощи команды сборки. В процессе выполнения создаются слои, соответствующие каждой инструкции в Dockerfile. По завершении формируется итоговый образ, который можно запускать как контейнер.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥2