Python академия
7.4K subscribers
2.49K photos
5 videos
322 links
Учи Python быстро и легко.

Ежедневно публикуем практические задачи, разборы, готовые решения, объяснения логики, советы по алгоритмам.

Подходит для прокачки навыков кодирования.

По всем вопросам @evgenycarter
Download Telegram
🚀 Ускоряем массовые запросы: Sessions

Представьте, что вам нужно скачать 100 страниц сайта. Если делать это через обычный requests.get(), каждый раз будет происходить следующее:

1. Открытие соединения (TCP Handshake).
2. Настройка шифрования (SSL/TLS).
3. Отправка данных.
4. Закрытие соединения.

Это очень долго! 🐢

Решение: Session
Сессия позволяет переиспользовать одно и то же соединение для множества запросов. Это дает огромный прирост скорости.

Пример:


import requests

# Создаем сессию
with requests.Session() as s:
# Можно сразу настроить заголовки для всех запросов
s.headers.update({'User-Agent': 'my-app/0.0.1'})

for i in range(5):
# Используем s.get вместо requests.get
r = s.get(f"https://httpbin.org/get?page={i}")
print(f"Запрос {i} выполнен!")



💡Сессия также запоминает куки (cookies). Если вы авторизовались на сайте в первом запросе, сессия "запомнит" это для следующих.

Используйте сессии, если делаете больше двух запросов к одному сайту!

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍4
🛑 Твой код завис навсегда? Исправляем!

Знаете ли вы, что по умолчанию библиотека requests ждет ответа от сервера бесконечно?

Если сервер, к которому вы обращаетесь, "упал" или просто завис, ваш Python-скрипт тоже встанет и будет висеть вечно, пока вы не убьете процесс вручную. В продакшене (да и в пет-проектах) это недопустимо! 🚫

Решение: Всегда используйте timeout

Параметр timeout указывает, сколько секунд ждать ответа, прежде чем выбросить ошибку и продолжить работу.

Пример безопасного кода:


import requests
from requests.exceptions import Timeout, ConnectionError

url = "https://httpbin.org/delay/5" # Сервер ответит через 5 сек

try:
# Ставим таймаут 2 секунды.
# Если сервер не ответит за 2 сек — скрипт не зависнет!
response = requests.get(url, timeout=2)
response.raise_for_status() # Проверка на ошибки 4xx/5xx
print("Успех:", response.json())

except Timeout:
print(" Ошибка: Сервер отвечает слишком долго!")
except ConnectionError:
print("🔌 Ошибка: Нет интернета или сайт недоступен.")
except Exception as e:
print(f"Что-то пошло не так: {e}")



💡 Совет: Хорошей практикой считается ставить таймаут в диапазоне 3–10 секунд для обычных запросов.

Проверьте свои старые проекты, есть ли там timeout? Если нет, самое время добавить! 👨‍💻

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍6
🚀 Синхронность vs Асинхронность: Ускоряем код в 10 раз

Представьте, что вы стоите в очереди в Макдональдс.

🐢 Синхронный подход (как requests):
Кассир принимает ваш заказ, уходит на кухню, ждет, пока пожарится картошка, наливает колу, отдает заказ вам и только потом обращается к следующему человеку в очереди. Медленно? Ужасно медленно!

⚡️ Асинхронный подход (как aiohttp):
Кассир принимает заказ, кричит на кухню "Свободная касса!" и сразу берет заказ у следующего, пока ваш готовится. Работа идет параллельно.

В Python для этого используется библиотека aiohttp и ключевые слова async / await.

Сравним на практике:
Допустим, нам нужно скачать 10 страниц.


import asyncio
import aiohttp
import time

# Функция для одного запроса
async def fetch_page(session, url):
async with session.get(url) as response:
return response.status

async def main():
urls = ["https://google.com" for _ in range(10)] # 10 запросов

async with aiohttp.ClientSession() as session:
tasks = []
for url in urls:
# Создаем задачи, но еще не запускаем их ожидание
tasks.append(fetch_page(session, url))

# Запускаем все задачи одновременно!
await asyncio.gather(*tasks)

start = time.time()
# Запуск асинхронного цикла
asyncio.run(main())
print(f"Время выполнения: {time.time() - start:.2f} сек")



Результат:
Если requests будет делать это ~10 секунд (по 1 сек на запрос), то aiohttp справится за ~1.2 секунды. Он просто отправит все 10 запросов разом и будет ждать ответы.

🛠 Установка:
pip install aiohttp

⚠️ Важно: Не используйте это для атак на сайты (DDoS). Сервер может забанить вас за слишком большое количество запросов в секунду.

Сохраняй, пригодится для парсинга больших объемов данных! 🔥

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍3😱1🥴1
🤖 Твой первый Telegram-бот на aiogram

Мы уже выяснили, что асинхронность это круто. А где она используется чаще всего? Конечно, в Telegram-ботах!

Сегодня напишем "Эхо-бота" - это как "Hello World" в мире ботов. Он будет просто повторять всё, что мы ему напишем.

🛠 Подготовка:

1. Идем к «отцу всех ботов» - @BotFather.
2. Пишем /newbot, даем имя и юзернейм.
3. Получаем API TOKEN (длинная строка символов).
4. Устанавливаем библиотеку: pip install aiogram

💻 Код (всего 20 строк):


import asyncio
import logging
from aiogram import Bot, Dispatcher, types
from aiogram.filters import CommandStart

# Вставь сюда токен от BotFather
TOKEN = "ВАШ_ТОКЕН_ЗДЕСЬ"

# Включаем логирование, чтобы видеть ошибки в консоли
logging.basicConfig(level=logging.INFO)

# Создаем объекты бота и диспетчера
bot = Bot(token=TOKEN)
dp = Dispatcher()

# 1. Обработчик команды /start
@dp.message(CommandStart())
async def cmd_start(message: types.Message):
await message.answer("Привет! Я твой первый бот на aiogram 🚀")

# 2. Обработчик любого текста (Эхо)
@dp.message()
async def echo_handler(message: types.Message):
# Метод .answer() отправляет ответ в тот же чат
await message.answer(f"Ты написал: {message.text}")

# Запуск процесса опроса (polling)
async def main():
await dp.start_polling(bot)

if __name__ == "__main__":
asyncio.run(main())



🧩 Разбор магии:

🟢Dispatcher (dp) - это мозг бота. Он получает сообщения от Telegram и решает, какой функции их передать.
🟢@dp.message(...) - это декораторы. Они "фильтруют" сообщения.
🟢await message.answer(...) - асинхронная отправка сообщения. Пока бот отправляет ответ тебе, он может параллельно обрабатывать сообщения от других пользователей!

Скопируй, вставь токен и запусти. Поздравляю, ты только что создал своего бота! 🎉

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
Please open Telegram to view this post
VIEW IN TELEGRAM
👍43🤔1
🎛 Делаем бота удобным: Кнопки и Меню

Никто не любит вводить команды вручную (/settings, /help). Пользователи любят тыкать пальцем! В Telegram есть два вида кнопок:

1. Reply-кнопки: Находятся под полем ввода. Идеальны для главного меню.
2. Inline-кнопки: Прикреплены к конкретному сообщению. Идеальны для ссылок, лайков или навигации.

Давайте добавим оба вида в нашего бота!

🛠 Код:


from aiogram.types import KeyboardButton, InlineKeyboardButton
from aiogram.utils.keyboard import ReplyKeyboardBuilder, InlineKeyboardBuilder

# --- 1. Создаем Reply-клавиатуру (Главное меню) ---
def get_main_menu():
# Инициализируем билдер
builder = ReplyKeyboardBuilder()

# Добавляем кнопки
builder.add(KeyboardButton(text="🔥 Профиль"))
builder.add(KeyboardButton(text="⚙️ Настройки"))
builder.add(KeyboardButton(text="📈 Статистика"))

# Настраиваем сетку: 2 кнопки в первом ряду, 1 во втором
builder.adjust(2, 1)

# resize_keyboard=True делает кнопки компактными
return builder.as_markup(resize_keyboard=True)

# --- 2. Создаем Inline-клавиатуру (Ссылка) ---
def get_url_keyboard():
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(
text="↗️ Подписаться на канал",
url="https://t.me/pythonofff"
))
return builder.as_markup()

# --- Используем в хэндлере ---
@dp.message(Command("start"))
async def cmd_start(message: types.Message):
await message.answer(
"Привет! Вот твое меню 👇",
reply_markup=get_main_menu()
)

await message.answer(
"А это инлайн-кнопка со ссылкой:",
reply_markup=get_url_keyboard()
)



💡 В чем сила Builders?
Метод .adjust(2, 1) позволяет одной строчкой настроить "сетку" кнопок. Раньше для этого приходилось писать сложные циклы списков!

Сохраняй, чтобы не гуглить синтаксис каждый раз! 💾

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍3
Магия Python: Как работают декораторы?

Вы постоянно видите их в коде: @bot.message_handler, @app.route, @staticmethod. Но что на самом деле делает этот символ @?

В Python функции - это объекты. Их можно передавать в другие функции, как обычные переменные.
Декоратор - это просто функция, которая принимает другую функцию, "обертывает" её в дополнительный код и возвращает обратно.

Представьте, что у вас есть подарок (функция). Декоратор - это упаковочная бумага. Подарок внутри тот же, но теперь он выглядит и ведет себя немного иначе.

Пример: Декоратор-логгер
Допустим, мы хотим автоматически писать в консоль, когда функция запустилась и когда завершилась.


def my_logger(func):
def wrapper():
print("📢 Запускаю функцию...")
func() # Вызываем оригинальную функцию
print(" Функция отработала!")
return wrapper

# Применяем декоратор
@my_logger
def say_hello():
print("Привет, мир!")

# Вызываем
say_hello()



Результат в консоли:


📢 Запускаю функцию...
Привет, мир!
Функция отработала!



🤯 Что происходит под капотом?
Синтаксис с @ - это просто "сахар" (упрощение). На самом деле Python делает вот это:


# Это:
@my_logger
def say_hello(): ...

# Равносильно этому:
def say_hello(): ...
say_hello = my_logger(say_hello)



А как это работает в ботах (@dp.message)?
В библиотеках типа aiogram декораторы часто используются для регистрации.
Когда вы пишете @dp.message(), декоратор не меняет код вашей функции, а сообщает Диспетчеру: "Эй, запомни эту функцию! Если придет сообщение, нужно запустить именно её".

Итог: Декораторы нужны, чтобы добавить логику вокруг функции (проверка прав доступа, замер скорости, логирование), не меняя её код внутри.

Ставь ❤️, если магия рассеялась и стало понятнее!

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
7👍2
Lambda: Функции-однострочники

В Python часто бывают ситуации, когда вам нужна маленькая функция всего один раз. Например, чтобы отсортировать список по хитрому правилу.

Писать для этого полноценный def, придумывать имя и занимать 3 строки кода - лень и некрасиво. Тут на сцену выходят Lambda-функции (они же анонимные функции).

Синтаксис прост:
lambda аргументы: выражение

Сравните:


# Классический способ
def square(x):
return x ** 2

# Lambda-стиль
sq = lambda x: x ** 2



🔥 Где это реально нужно?
Чаще всего - внутри функций sorted(), min(), max(), map() или filter().

Пример из жизни:
У нас есть список студентов и их баллов. Нам нужно отсортировать их не по имени, а по баллам.


students = [
("Антон", 50),
("Вика", 90),
("Борис", 75)
]

# Сортируем по 2-му элементу кортежа (x[1])
sorted_students = sorted(students, key=lambda x: x[1])

print(sorted_students)
# [('Антон', 50), ('Борис', 75), ('Вика', 90)]



Внутри key= мы на лету создали функцию, которая принимает студента x и возвращает его балл x[1]. sorted использует это значение для сортировки.

🛑 Когда НЕ надо использовать Lambda:
Не присваивайте лямбды переменным, если планируете использовать их много раз.


# Плохо (нарушает PEP 8):
f = lambda x: x * 2

# Хорошо:
def f(x):
return x * 2



Лямбды созданы, чтобы быть "одноразовыми"!

Используете лямбды или предпочитаете старый добрый def? 👇

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍31
Магия одной строки: List Comprehension

Вы всё еще пишете циклы для заполнения списков вот так?


squares = []
for x in range(10):
squares.append(x ** 2)



Это работает, но это "старая школа". В Python есть способ сделать это элегантнее, быстрее и понятнее - Списковые включения.

Как это выглядит:
[выражение for элемент in список]

Тот же самый код выше превращается в:


squares = [x ** 2 for x in range(10)]



🚀 Почему это круто?

1. Лаконичность: Меньше кода меньше багов.
2. Скорость: Это работает быстрее обычного цикла for, потому что метод .append() не вызывается на каждой итерации (оптимизация на уровне C).

Уровень PRO: Добавляем условия
Внутри можно использовать if, чтобы фильтровать данные на лету.

Задача: Оставить только четные числа и умножить их на 10.


numbers = [1, 2, 3, 4, 5, 6]

# Читается как английское предложение:
# "x умножить на 10 для каждого x в numbers, если x четный"
result = [x * 10 for x in numbers if x % 2 == 0]

print(result)
# [20, 40, 60]



💡 Совет: Не увлекайтесь! Если ваше выражение не влезает в одну строку или становится слишком сложным (например, вложенные циклы), лучше вернитесь к старому доброму for. Читаемость важнее понтов!

Часто используете эту конструкцию? Ставьте 🔥!

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍6🔥3
💾 Бесконечность не предел: Магия генераторов (yield)

В прошлом посте мы восхищались списковыми включениями [...]. Но у них есть фатальный недостаток: они создают весь список в памяти сразу.

Представьте, что вам нужно обработать файл весом 10 ГБ. Если вы попытаетесь загрузить его в список read_lines(), ваш компьютер скажет "Memory Error" и программа упадет. 💥

Тут на помощь приходят Генераторы.

В чем отличие?

🟢📦 Список (list): Хранит все элементы в памяти. Как готовая книга - все страницы уже напечатаны.

🟢⚙️ Генератор: Не хранит данные. Он генерирует следующее значение только тогда, когда вы его попросите. Как рассказчик, который придумывает историю на ходу.

Как создать?

1. Генераторное выражение: Просто замените квадратные скобки [] на круглые ().


import sys

# Список (создает миллион чисел в памяти)
my_list = [x for x in range(1_000_000)]
print(f"List size: {sys.getsizeof(my_list)} bytes")
# ~8 000 000 байт (8 МБ)

# Генератор (просто формула)
my_gen = (x for x in range(1_000_000))
print(f"Gen size: {sys.getsizeof(my_gen)} bytes")
# ~100 байт (!!!)



Разница колоссальная! Генератор весит копейки, даже если в нем миллиард чисел.

2. Функция с yield:
Если логика сложная, используем функцию. Вместо return (который убивает функцию и возвращает результат), пишем yield (который ставит на паузу и отдает значение).


def endless_numbers():
n = 0
while True: # Бесконечный цикл!
yield n
n += 1

# Можно брать по одному
gen = endless_numbers()
print(next(gen)) # 0
print(next(gen)) # 1
# Программа не зависнет, несмотря на while True



🚀 Итог: Если вам не нужен доступ ко всем элементам сразу (например, по индексу list[10]), а вы просто перебираете их в цикле for - всегда используйте генераторы.

Знали про sys.getsizeof? Проверьте свои списки! 😉

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🔄 Хватит писать range(len(...))!

Новички часто переносят привычки из других языков в Python. Самый частый пример это циклы.

Как пишет новичок:
Если нужно получить элемент списка И его индекс:


names = ["Анна", "Борис", "Олег"]

# Стиль C/Java
for i in range(len(names)):
print(f"{i}: {names[i]}")



Это работает, но выглядит громоздко и не "по-питоньи".

1. Используем enumerate
Эта функция сразу выдает и счетчик, и само значение. Код становится чище!


for i, name in enumerate(names, start=1):
print(f"{i}: {name}")



💡 Фишка: Аргумент start=1 позволяет начать нумерацию не с нуля, а с единицы (полезно для вывода списков пользователю).



Как пишет новичок:
Если нужно пройтись сразу по двум спискам (имена и зарплаты):


salaries = [100, 200, 300]

for i in range(len(names)):
print(f"{names[i]} получает {salaries[i]}")



2. Используем zip
Функция zip (молния 🤐) "сшивает" несколько списков в один, как застежка.


for name, salary in zip(names, salaries):
print(f"{name} получает {salary}")



⚠️ Важно: Если списки разной длины, zip остановится, когда закончится самый короткий из них. Остальные "хвосты" будут отброшены (если не использовать zip_longest из itertools).


📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍11
Изменение в контекстных менеджерах

Как вы знаете, для удобного управления жизненным циклом ресурсов в python существуют контекстные менеджеры. Но до недавнего времени, чтобы открыть несколько ресурсов в рамках одной области видимости with нам требовалось либо прописывать их в одну строку либо использовать синтаксис переноса - обратный слеш \ (но это противоречит PEP8).

С выходом python 3.10 появился новый синтаксис, позволяющий в круглых скобках указывать несколько контекстных менеджеров. А также мы можем использовать переменную созданную одним контекстным менеджером в рамках следующего за ним менеджера.

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

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍3
🔮 Магия Множеств: set круче списков

Представьте задачу: у вас есть два списка подписчиков - из Instagram и из Telegram.

1. Нужно найти тех, кто подписан и там, и там.
2. Нужно найти тех, кто есть в Telegram, но нет в Instagram.

Способ новичка (Циклы):


insta = ["max", "leo", "kate"]
tg = ["leo", "alex", "kate"]

common = []
for user in insta:
if user in tg:
common.append(user)



Долго, скучно, медленно (O(n*m)).

Способ Профи (Множества):
Множества в Python работают как круги Эйлера в математике.


# Создаем множества
insta = {"max", "leo", "kate"}
tg = {"leo", "alex", "kate"}

# 1. Пересечение (&) - кто есть ВЕЗДЕ
print(insta & tg)
# Вывод: {'leo', 'kate'}

# 2. Разность (-) - кто есть в TG, но НЕТ в Insta
print(tg - insta)
# Вывод: {'alex'}

# 3. Симметричная разность (^) - уникальные для каждой платформы
print(insta ^ tg)
# Вывод: {'max', 'alex'}



💡 И самый главный трюк:
Как быстро убрать дубликаты из списка? Просто превратите его в множество!


ids = [1, 2, 2, 3, 3, 3]
unique_ids = list(set(ids))
print(unique_ids) # [1, 2, 3]



🚀 Скорость: Поиск в множестве (val in my_set) происходит почти мгновенно (), в отличие от списка, который нужно перебирать целиком.

Используйте силу математики, чтобы писать меньше кода! 🧠

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍7👏2
Логирование

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

Python поставляется для этих целей с гибким модулем logging. Для создания объекта Logger, вызываем функцию getLogger, передавая в нее имя логера.

Созданный объект Logger предоставляет методы для записи сообщений разного уровня (DEBUG, INFO, WARNING, ERROR, CRITICAL), что удобно для поиска нужной информации с применением фильтров.

По умолчанию в logging задан уровень WARNING, это означает, что сообщения уровня DEBUG и INFO будут игнорироваться при записи в лог. Изменить данное поведение можно с помощью метода setLevel, передав минимальный уровень, который будет отлавливаться.

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

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍2
Пакет со всеми алгоритмами

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

Список всех алгоритмов с ссылками на их исходный код можете найти здесь. Такой пакет можно использовать и на практике, и как справочник на всякий случай.

Например, можете посмотреть пример использования сортировки слиянием на картинке выше.

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍3
♾️ Бесконечные аргументы: Магия Звездочек

Замечали, что в функцию print() можно передать сколько угодно значений?
print(1, "a", [1,2], True) - и она не ломается!

Как сделать такую же всеядную функцию самому? Нам понадобятся операторы распаковки: * и **.

📦 1. *args (Позиционные аргументы)
Если поставить одну звездочку перед именем аргумента (обычно называют args), Python соберет все переданные значения в кортеж (tuple).


def sum_all(*args):
# args превратится в (1, 2, 3, 4, 5)
print(f"Тип: {type(args)}")
return sum(args)

print(sum_all(1, 2, 3, 4, 5))
# Вывод: 15



Теперь ваша функция может принимать хоть 0, хоть 100 чисел!

🗝️ 2. **kwargs (Именованные аргументы)
Две звездочки (обычно kwargs - keyword arguments) собирают всё в словарь (dict). Это идеально для настроек или сложных объектов.


def create_profile(**kwargs):
# kwargs превратится в {'name': 'Alex', 'age': 25}
for key, value in kwargs.items():
print(f"{key}: {value}")

create_profile(name="Alex", age=25, city="Minsk")



🔄 Обратная магия: Распаковка
Звездочки работают и "на выход"! Если у вас есть список, и вы хотите передать его содержимое как отдельные аргументы:


numbers = [1, 2, 3]

# Вместо print(numbers[0], numbers[1], numbers[2])
# Пишем просто:
print(*numbers)
# Вывод: 1 2 3



⚠️ Важно: Порядок в определении функции строгий!

1. Обычные аргументы (a, b)
2. *args
3. **kwargs

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

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
6👍4
Как найти индексы всех вхождений элемента в списке

А что, если искомое значение встречается в списке несколько раз и мы хотим узнать индексы всех этих элементов? Метод index() выдаст нам индекс только первого вхождения.

В этом фрагменте кода мы перебираем индексы списка в цикле for и при помощи range(). Далее мы проверяем значение элемента под каждым индексом на равенство «Math«. Если значение элемента — «Math«, мы сохраняем значение индекса в списке.

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍2
Как искать индекс элемента, которого, возможно, нет в списке

Бывает, нужно получить индекс элемента, но мы не уверены, есть ли он в списке.

Если попытаться получить индекс элемента, которого нет в списке, метод index() вызовет ошибку ValueError. При отсутствии обработки исключений ValueError вызовет аварийное завершение программы. Такой исход явно не является хорошим и с ним нужно что-то сделать.

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍3
Проверка истинности объекта класса

Для того, чтобы определить поведение при проверке на истинность объектов классов в python3 есть "магический" метод __bool__(), который был добавлен на замену устаревшему __nonzero__() в python2.

Если данный метод определен в классе, то он будет вызываться при каждой проверке объекта на истинность а также с помощью функции bool(). Метод должен возвращать False или True.

Если __bool__() не определен, будет вызываться метод __len__(), если он определен, и, соответственно, объект будет считаться истинным, если результат __len__() не будет равен нулю. Если в классе не определены ни __len__(), ни __bool__(), все его экземпляры будут истинными.

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍2
В чем сложность массивов и хешмапов в python

Сложность массивов (списков) и хешмапов (словари) в Python зависит от операций, которые вы выполняете. Вот основные операции и их временные сложности:

Массивы (списки)
В Python массивы представлены списками (`list`). Сложность операций со списками зависит от конкретного действия:

- Доступ по индексу: O(1) — доступ к элементу списка по индексу выполняется за постоянное время, так как список реализован как динамический массив.
- Добавление элемента:
- В конец списка: O(1) амортизированное — добавление элемента в конец списка обычно занимает постоянное время, так как динамический массив увеличивает свой размер экспоненциально.
- В начало или середину списка: O(n) — если вы добавляете элемент в начало или в середину списка, это требует сдвига всех последующих элементов, что занимает линейное время.
- Удаление элемента:
- Из конца списка: O(1) — удаление последнего элемента занимает постоянное время.
- Из начала или середины списка: O(n) — удаление элемента требует сдвига оставшихся элементов.
- Поиск элемента: O(n) — поиск элемента в списке требует обхода всего списка в худшем случае.

Хешмапы (словари)
Хешмапы в Python реализованы с помощью словарей (`dict`). Они используют хеширование для обеспечения быстрого доступа к элементам по ключу:

- Доступ по ключу: O(1) — доступ к элементу по ключу выполняется за постоянное время благодаря хешированию.
- Добавление элемента: O(1) — добавление нового элемента выполняется за постоянное время, если не происходит коллизий.
- Удаление элемента: O(1) — удаление элемента по ключу также выполняется за постоянное время.
- Поиск по ключу: O(1) — поиск элемента по ключу занимает постоянное время.

Сравнение
- Списки полезны, когда важен порядок элементов и доступ к ним по индексу. Однако некоторые операции (например, вставка или удаление в середине списка) могут быть медленными из-за необходимости сдвига элементов.
- Словари обеспечивают быстрый доступ к элементам по ключу, что делает их более эффективными для операций, связанных с поиском и манипуляцией данных по ключу.

Таким образом, основная сложность использования списков и словарей в Python связана с выбором правильной структуры данных для конкретной задачи, чтобы минимизировать временные затраты на ключевые операции.

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍2
📝 Код понятный без слов: Аннотации типов

Python - язык с динамической типизацией. Это значит, что переменная x может сначала быть числом 5, а через секунду стать строкой "Привет".
Удобно? Да. Опасно? Еще как! 💣

Представьте функцию:


def process(data):
return data * 2



Что такое data? Число? Строка? Список?

• Если число 10 -> вернет 20.

• Если строка "Hi" -> вернет "HiHi".

• Если список [1] -> вернет [1, 1].

Чтобы не гадать, используйте Аннотации типов (Type Hints).

Как это выглядит:


# Аргумент name - строка, возраст - число
# Функция возвращает строку (-> str)
def greeting(name: str, age: int) -> str:
return f"Привет, {name}! Тебе {age} лет."



🚀 Зачем это нужно?

1. Автодополнение (IDE): Ваш редактор (PyCharm, VS Code) сразу поймет, что name - это строка, и предложит методы .upper(), .split() и т.д. Без аннотаций он будет "слепым".

2. Документация: Коллегам не нужно читать весь код функции, чтобы понять, что в неё передавать.

3. Поиск ошибок: Инструменты вроде mypy проверят код до запуска и скажут: "Эй, ты пытаешься передать число в функцию, которая ждет строку!"

💡 Продвинутый уровень (Python 3.10+):
Можно указывать сложные структуры.


# Список чисел
def sum_list(numbers: list[int]) -> int:
return sum(numbers)

# Или число, ИЛИ строка (Union)
def magic(val: int | str):
print(val)



⚠️ Важно: Python игнорирует аннотации при запуске! Это просто "подсказки". Если вы напишете x: int, но передадите строку, программа запустится (и, скорее всего, упадет с ошибкой внутри). Аннотации это для вас и редактора, а не для интерпретатора.

Начните добавлять типы в свои функции, и вы увидите, насколько удобнее станет писать код! 😎

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
4👍4
Как использовать ключевое слово global в Python

По умолчанию Python думает, что внутри функции вы хотите использовать локальную переменную.

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

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍2