Python академия – Telegram
Python академия
7.52K subscribers
2.48K photos
5 videos
300 links
Учи Python быстро и легко.

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

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

По всем вопросам @evgenycarter
Download Telegram
Генерация капчи

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

Все максимально просто, за нас по сути все делает уже написанный в модуле код. Создаем объект изображения ImageCaptcha, на который будет нанесен текст. После чего вызываем метод write с заданным текстом и именем файла, в который будет записано изображение.

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Получаем исходный код объекта

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

Сегодня поговорим только про getsource(), который возвращает весь исходный код функции, класса или модуля в виде строки.

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

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍1
Свойства Concrete Paths

Поскольку Concrete Paths является подклассом PurePath, мы можем использовать все свойства PurePath().

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

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Проверяем тип объекта

Раз уж в предыдущем посте затронули модуль inspect, то давайте еще поговорим про некоторые его возможности.

Функции ismodule(), isclass(), ismethod() и isfunction() проверяют переданный объект на то, является ли он модулем, классом, методом или функцией соответственно.

📲 Мы в MAX

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

Согласно официальной документации, метод .mkdir() принимает три аргумента. Мы пока сосредоточимся только на parents и exists_ok.

Оба аргумента имеют значение False по умолчанию. Аргумент parents вызывает ошибку FileNotFound в случае отсутствия родителя, тогда как exists_ok вызывает ошибку FileExists, если данный каталог уже существует.

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🐍 Ловушка с аргументами по умолчанию

Посмотрите на код ниже. Как вы думаете, что выведет второй вызов функции?


def append_to(num, target=[]):
target.append(num)
return target

print(append_to(1))
print(append_to(2))



Варианты ответа:

1. [1] и [2]
2. [1] и [1, 2]
3. Ошибка SyntaxError

⬇️ Правильный ответ и объяснение ⬇️


Правильный ответ: 2 — [1] и [1, 2]

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

Как исправить?
Используйте None в качестве значения по умолчанию:


def append_to(num, target=None):
if target is None:
target = []
target.append(num)
return target


📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍7
Объединяем словари в Python 3.9+

Раньше для объединения двух словарей нам приходилось использовать метод .update() или распаковку **kwargs.

Начиная с Python 3.9, появился оператор объединения |. Это делает код намного чище!

Пример:


x = {"key1": "value1 from x", "key2": "value2 from x"}
y = {"key2": "value2 from y", "key3": "value3 from y"}

# Старый способ (до 3.9)
z = {**x, **y}

# Новый способ (Python 3.9+)
z = x | y

print(z)
# {'key1': 'value1 from x', 'key2': 'value2 from y', 'key3': 'value3 from y'}



💡При совпадении ключей (в примере key2), побеждает значение из последнего словаря (справа от оператора).

Сохраняй, чтобы писать чистый код! 💾

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍4
🧠 List vs Set: Где искать быстрее?

Представьте, у вас есть массив из 1 миллиона чисел, и нужно проверить, есть ли в нем число 999999.

Что использовать: список (list) или множество (set)?

Сравнение сложности:

▪️ List (in operator): Сложность . Python перебирает элементы по одному, пока не найдет нужное. Если список огромный, это долго.
▪️ Set (in operator): Сложность (в среднем). Множества реализованы как хеш-таблицы. Python вычисляет хеш числа и сразу "прыгает" в нужную ячейку памяти.

Тест скорости:


import time

data_list = list(range(10_000_000))
data_set = set(data_list)

# Поиск в списке
start = time.time()
print(9999999 in data_list)
print(f"List time: {time.time() - start:.5f} sec")

# Поиск в множестве
start = time.time()
print(9999999 in data_set)
print(f"Set time: {time.time() - start:.5f} sec")


🚀 Итог: Если вам нужно часто проверять наличие элемента в большой коллекции - всегда конвертируйте её в set.

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍43
🕵️‍♂️ Дебаг без боли: секрет f-строк

Признавайтесь, вы тоже часто пишете такие принты, чтобы проверить значение переменной?


name = "Alex"
age = 25

print("name =", name)
print("age =", age)



Это работает, но писать каждый раз название переменной в кавычках — долго и лениво. В Python есть супер-фишка для f-строк, о которой многие забывают. Просто добавьте знак равно = после имени переменной внутри фигурных скобок!

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


name = "Alex"
age = 25

print(f"{name=}")
print(f"{age=}")



Вывод:


name='Alex'
age=25



🔥 Почему это круто: Python сам подставляет имя переменной и её значение. Экономит кучу времени при поиске багов!

Знали про этот трюк? Ставьте ❤️, если уже пользуетесь!

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍11
🧩 Задача: Проверка на анаграмму

Анаграммы, это слова, состоящие из одних и тех же букв, но в разном порядке. Например: ток и кот.
Как проверить, являются ли две строки анаграммами, максимально эффективно?

Вариант 1 (В лоб): Сортировка
Мы можем отсортировать обе строки и сравнить их.


def is_anagram(s1, s2):
return sorted(s1) == sorted(s2)


Это работает, но сложность сортировки - O(N log N) . Можно ли быстрее? 🤔

Вариант 2 (Оптимальный): Подсчет символов
Используем словарь (или Counter), чтобы посчитать, сколько раз встречается каждая буква. Сложность такого решения - O(N) , то есть линейная (самая быстрая).


from collections import Counter

def is_anagram_fast(s1, s2):
return Counter(s1) == Counter(s2)

print(is_anagram_fast("listen", "silent")) # True



💡 Совет: На собеседованиях всегда предлагайте сначала решение с сортировкой (оно проще), а потом удивляйте интервьюера знанием сложности алгоритмов и вариантом с хеш-таблицей (Counter)!

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
6👍3
☠️ Самая частая ошибка новичков в Python

Посмотрите на этот код. Как думаете, что пошло не так?


def add_item(item, basket=[]):
basket.append(item)
return basket

print(add_item("Яблоко"))
print(add_item("Банан"))



Ожидание:
['Яблоко']
['Банан']


Реальность:
['Яблоко']
['Яблоко', 'Банан'] 😱


Почему так произошло?
В Python аргументы по умолчанию создаются один раз при запуске программы (когда интерпретатор читает def), а не при каждом вызове функции. Поэтому список basket - это один и тот же объект в памяти для всех вызовов!

Как писать правильно:
Используйте None как маркер пустого значения.


def add_item(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket


Сохраняй пост, чтобы не наступать на эти грабли! 💾

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
👍4🔥3🥱1
🌐 Твой первый API запрос на Python

Если нужно получить данные с сайта (курс валют, погоду или список постов), библиотека requests - твой лучший друг. Она делает общение с интернетом невероятно простым.

Допустим, мы хотим получить шутку с бесплатного API.

Как это сделать в 3 строчки:


import requests

# 1. Делаем GET-запрос по адресу
url = "https://official-joke-api.appspot.com/random_joke"
response = requests.get(url)

# 2. Проверяем, что всё ок (код 200)
if response.status_code == 200:
# 3. Превращаем ответ из JSON в словарь Python
data = response.json()
print(f"Шутка: {data['setup']} - {data['punchline']}")
else:
print("Ошибка соединения 😢")



📌 Разбор:

🟢.status_code - показывает, как прошел запрос (200 = ОК, 404 = Не найдено, 500 = Ошибка сервера).
🟢.json() - это магия, которая превращает строку текста от сервера сразу в понятный Python-словарь.

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🛠 Передаем параметры красиво

Частая ошибка новичков, склеивать параметры запроса вручную прямо в URL.

Как НЕ надо делать:


user_id = 12
# Плохо: сложно читать, легко ошибиться, проблемы со спецсимволами
url = "https://jsonplaceholder.typicode.com/posts?userId=" + str(user_id)
r = requests.get(url)



Как НАДО делать:
Используйте аргумент params. Библиотека сама всё склеит и, что важно, правильно закодирует символы (например, заменит пробелы на %20).


import requests

url = "https://jsonplaceholder.typicode.com/posts"
# Параметры кладем в словарь
my_params = {
"userId": 1,
"_limit": 5
}

# Передаем словарь в функцию
response = requests.get(url, params=my_params)

print(response.url)
# Вывод: .../posts?userId=1&_limit=5



Это делает код чище, безопаснее и профессиональнее. 😎

📲 Мы в MAX

Подписывайтесь на канал 👉@pythonofff
5👍1
🚀 Ускоряем массовые запросы: 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
👍41🤔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://news.1rj.ru/str/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
6👍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🔥2