Перейти к основному содержимому

Простые приложения на Python

Разработчику Архитектору

Простые приложения на Python

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


Генератор паролей

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

import random
import string

def generate_password(length=12):
# Определение наборов символов
lowercase = string.ascii_lowercase
uppercase = string.ascii_uppercase
digits = string.digits
symbols = string.punctuation

# Объединение всех наборов в одну строку
all_chars = lowercase + uppercase + digits + symbols

# Генерация случайного пароля заданной длины
password = ''.join(random.choice(all_chars) for _ in range(length))

return password

if __name__ == "__main__":
user_length = int(input("Введите длину пароля: "))
generated = generate_password(user_length)
print(f"Сгенерированный пароль: {generated}")

Разбор:

  • Модули: string содержит предопределенные константы с буквами алфавита и цифрами. random отвечает за выбор случайных элементов.
  • Конкатенация строк: Оператор + объединяет строки в единый источник символов.
  • Генератор списков (Comprehension): Выражение (random.choice(...) for _ in range(...)) создает последовательность случайных символов.
  • Метод join: Строка "".join(...) собирает элементы списка в итоговую строку без разделителей.

Сортировщик текстового файла

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

def sort_file_lines(input_path, output_path):
try:
# Чтение файла в память
with open(input_path, 'r', encoding='utf-8') as file:
lines = file.readlines()

# Удаление лишних переносов строк и фильтрация пустых строк
clean_lines = [line.strip() for line in lines if line.strip()]

# Сортировка списка по алфавиту
sorted_lines = sorted(clean_lines)

# Запись отсортированных данных в новый файл
with open(output_path, 'w', encoding='utf-8') as file:
for line in sorted_lines:
file.write(line + '\n')

print(f"Файл успешно обработан. Строк: {len(sorted_lines)}")

except FileNotFoundError:
print("Ошибка: Файл не найден.")
except Exception as e:
print(f"Произошла ошибка: {e}")

if __name__ == "__main__":
input_file = "input.txt"
output_file = "output_sorted.txt"
sort_file_lines(input_file, output_file)

Разбор:

  • Контекстный менеджер (with): Гарантирует автоматическое закрытие файлов после завершения блока кода, даже при возникновении ошибок.
  • Обработка кодировки: Параметр encoding='utf-8' обеспечивает корректную работу с русским языком.
  • Метод strip: Удаляет пробелы и символы перевода строки по краям каждой строки.
  • Функция sorted: Возвращает новый отсортированный список, не изменяя исходный.

Консольный калькулятор

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

def calculate():
while True:
print("\nДоступные операции: +, -, *, / (или 'q' для выхода)")
operation = input("Выберите операцию: ").strip()

if operation == 'q':
break

if operation not in ['+', '-', '*', '/']:
print("Некорректная операция. Попробуйте снова.")
continue

try:
num1 = float(input("Введите первое число: "))
num2 = float(input("Введите второе число: "))

result = 0
if operation == '+':
result = num1 + num2
elif operation == '-':
result = num1 - num2
elif operation == '*':
result = num1 * num2
elif operation == '/':
if num2 == 0:
print("Ошибка: Деление на ноль невозможно.")
continue
result = num1 / num2

print(f"Результат: {result}")

except ValueError:
print("Ошибка: Введите корректные числа.")

if __name__ == "__main__":
calculate()

Разбор:

  • Бесконечный цикл (while True): Позволяет программе работать до явной команды выхода.
  • Условная логика (if/elif/else): Выбирает нужную математическую операцию.
  • Обработка исключений (try/except): Перехватывает ошибки преобразования текста в число (ValueError).
  • Типизация: Функция float() преобразует строковый ввод в вещественное число.

Трекер задач в JSON

Этот пример демонстрирует сериализацию (преобразование объектов в строку) и десериализацию (восстановление объектов из строки), а также работу с форматом JSON.

import json
import os

DATA_FILE = "Задачи.json"

def load_tasks():
if not os.path.exists(DATA_FILE):
return []
with open(DATA_FILE, 'r', encoding='utf-8') as f:
try:
return json.load(f)
except json.JSONDecodeError:
return []

def save_tasks(Задачи):
with open(DATA_FILE, 'w', encoding='utf-8') as f:
json.dump(Задачи, f, ensure_ascii=False, indent=4)

def add_task(task_name):
Задачи = load_tasks()
task_id = len(Задачи) + 1
new_task = {"id": task_id, "name": task_name, "completed": False}
Задачи.append(new_task)
save_tasks(Задачи)
print(f"Задача #{task_id} добавлена.")

def list_tasks():
Задачи = load_tasks()
if not Задачи:
print("Список задач пуст.")
return
print("\n--- Список задач ---")
for task in Задачи:
status = "✓" if task["completed"] else "✗"
print(f"[{status}] ID: {task['id']}, Задача: {task['name']}")

if __name__ == "__main__":
# Пример использования
add_task("Изучить Python")
add_task("Написать статью")
list_tasks()

Разбор:

  • Модуль json: Функция json.dump сохраняет объект в файл как строку JSON. Функция json.load читает файл и преобразует его обратно в словарь или список.
  • Параметр ensure_ascii=False: Сохраняет кириллицу в файле как есть, а не в виде экранированных последовательностей.
  • Структура данных: Задачи хранятся в списке словарей, где каждый словарь описывает одну задачу.
  • Проверка существования файла: Функция os.path.exists предотвращает ошибку чтения несуществующего файла.

Простой мессенджер

Клиент

Создайте файл client_gui.py:

import socket
import threading
import tkinter as tk
from tkinter import scrolledtext, messagebox, simpledialog

class ChatClientGUI:
def __init__(self, host='127.0.0.1', port=5555):
self.host = host
self.port = port
self.client_socket = None
self.nickname = None
self.running = False

# Создаём главное окно
self.root = tk.Tk()
self.root.title("Мессенджер")
self.root.geometry("600x500")
self.root.resizable(True, True)

# Настройка стилей
self.setup_ui()

# Подключаемся к серверу
self.connect_to_server()

def setup_ui(self):
"""Настройка интерфейса"""
# Верхняя панель с информацией
self.top_frame = tk.Frame(self.root, bg='gray', height=40)
self.top_frame.pack(fill=tk.X, padx=5, pady=5)

self.status_label = tk.Label(
self.top_frame,
text="Не подключён",
bg='gray',
fg='white',
font=('Arial', 10)
)
self.status_label.pack(side=tk.LEFT, padx=10)

# Кнопки управления
self.disconnect_btn = tk.Button(
self.top_frame,
text="Отключиться",
command=self.disconnect,
bg='red',
fg='white'
)
self.disconnect_btn.pack(side=tk.RIGHT, padx=5)

# Область отображения сообщений
self.chat_area = scrolledtext.ScrolledText(
self.root,
wrap=tk.WORD,
font=('Arial', 11),
bg='white',
fg='black'
)
self.chat_area.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

# Нижняя панель для ввода сообщений
self.bottom_frame = tk.Frame(self.root)
self.bottom_frame.pack(fill=tk.X, padx=5, pady=5)

# Поле ввода сообщения
self.message_entry = tk.Entry(
self.bottom_frame,
font=('Arial', 11),
state='disabled'
)
self.message_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
self.message_entry.bind('<Return>', self.send_message_event)

# Кнопка отправки
self.send_button = tk.Button(
self.bottom_frame,
text="Отправить",
command=self.send_message,
state='disabled',
bg='#4CAF50',
fg='white',
width=10
)
self.send_button.pack(side=tk.RIGHT)

# Меню
self.setup_menu()

def setup_menu(self):
"""Настройка меню"""
menubar = tk.Menu(self.root)
self.root.config(menu=menubar)

# Меню "Файл"
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Файл", menu=file_menu)
file_menu.add_command(label="Подключиться заново", command=self.reconnect)
file_menu.add_separator()
file_menu.add_command(label="Выход", command=self.on_closing)

# Меню "Справка"
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Справка", menu=help_menu)
help_menu.add_command(label="О программе", command=self.show_about)
help_menu.add_command(label="Команды", command=self.show_commands)

def connect_to_server(self):
"""Подключение к серверу"""
try:
# Запрашиваем ник
self.nickname = simpledialog.askstring(
"Никнейм",
"Введите ваш никнейм:",
parent=self.root
)

if not self.nickname:
self.root.quit()
return

# Подключаемся к серверу
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client_socket.connect((self.host, self.port))

# Получаем запрос на ввод ника
initial = self.client_socket.recv(1024).decode('utf-8')
if initial == "NICK":
self.client_socket.send(self.nickname.encode('utf-8'))

# Обновляем интерфейс
self.status_label.config(text=f"Подключён как: {self.nickname}")
self.message_entry.config(state='normal')
self.send_button.config(state='normal')
self.disconnect_btn.config(text="Отключиться")

# Запускаем поток для приёма сообщений
self.running = True
receive_thread = threading.Thread(target=self.receive_messages, daemon=True)
receive_thread.start()

# Добавляем сообщение о подключении
self.add_message("Система", f"Вы подключились к серверу как {self.nickname}")

except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось подключиться к серверу:\n{str(e)}")
self.root.quit()

def receive_messages(self):
"""Получение сообщений от сервера"""
while self.running:
try:
message = self.client_socket.recv(1024).decode('utf-8')
if message:
# Разбираем сообщение для форматирования
if ": " in message and not message.startswith("Система"):
# Обычное сообщение от пользователя
parts = message.split(": ", 1)
if len(parts) == 2:
self.add_message(parts[0], parts[1])
else:
self.add_message("Сообщение", message)
else:
# Системное сообщение
self.add_message("Система", message)
else:
break
except:
if self.running:
self.add_message("Система", "Соединение с сервером потеряно")
self.disconnect()
break

def send_message(self):
"""Отправка сообщения"""
message = self.message_entry.get().strip()
if message and self.client_socket:
try:
if message == '/quit':
self.disconnect()
else:
self.client_socket.send(message.encode('utf-8'))
self.message_entry.delete(0, tk.END)
except:
self.add_message("Система", "Ошибка при отправке сообщения")
self.disconnect()

def send_message_event(self, event):
"""Обработчик события Enter"""
self.send_message()

def add_message(self, sender, message):
"""Добавление сообщения в чат"""
self.chat_area.config(state='normal')

# Форматируем сообщение
if sender == "Система":
self.chat_area.insert(tk.END, f"\n🔔 {message}\n", "Система")
self.chat_area.tag_config("Система", foreground="red", font=('Arial', 9, 'italic'))
else:
self.chat_area.insert(tk.END, f"\n{sender}: ", "sender")
self.chat_area.insert(tk.END, f"{message}\n", "message")
self.chat_area.tag_config("sender", foreground="blue", font=('Arial', 10, 'bold'))
self.chat_area.tag_config("message", foreground="black", font=('Arial', 10))

self.chat_area.see(tk.END)
self.chat_area.config(state='disabled')

def disconnect(self):
"""Отключение от сервера"""
self.running = False
if self.client_socket:
try:
self.client_socket.close()
except:
pass
self.client_socket = None

self.status_label.config(text="Отключён")
self.message_entry.config(state='disabled')
self.send_button.config(state='disabled')
self.disconnect_btn.config(text="Подключиться", command=self.reconnect)
self.add_message("Система", "Вы отключились от сервера")

def reconnect(self):
"""Переподключение к серверу"""
if self.client_socket:
self.disconnect()
self.connect_to_server()

def show_about(self):
"""Показать информацию о программе"""
about_text = """Мессенджер с графическим интерфейсом

Версия: 1.0
Язык: Python + tkinter
Функции:
• Поддержка множества пользователей
• Системные уведомления
• Отключение/подключение
• Команды: /quit

Для работы требуется сервер (server.py)"""
messagebox.showinfo("О программе", about_text)

def show_commands(self):
"""Показать доступные команды"""
commands_text = """Доступные команды:

/quit - Выход из чата (отключение)

Примечание:
Для приватных сообщений нужна доработка сервера."""
messagebox.showinfo("Команды", commands_text)

def on_closing(self):
"""Обработка закрытия окна"""
self.running = False
if self.client_socket:
try:
self.client_socket.close()
except:
pass
self.root.destroy()

def run(self):
"""Запуск приложения"""
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
self.root.mainloop()

if __name__ == "__main__":
app = ChatClientGUI()
app.run()

Сервер

Создайте файл server.py:

import socket
import threading

class ChatServer:
def __init__(self, host='127.0.0.1', port=5555):
self.host = host
self.port = port
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.clients = []
self.nicknames = []

def start(self):
self.server_socket.bind((self.host, self.port))
self.server_socket.listen()
print(f"Сервер запущен на {self.host}:{self.port}")
print("Ожидание подключений...")

while True:
client_socket, addr = self.server_socket.accept()
print(f"Подключение от {addr}")

client_socket.send("NICK".encode('utf-8'))
nickname = client_socket.recv(1024).decode('utf-8')

self.clients.append(client_socket)
self.nicknames.append(nickname)

print(f"Никнейм: {nickname}")
self.broadcast(f"{nickname} присоединился к чату!".encode('utf-8'))
client_socket.send("Подключён к серверу".encode('utf-8'))

thread = threading.Thread(target=self.handle_client, args=(client_socket, nickname))
thread.start()

def broadcast(self, message):
for client in self.clients:
try:
client.send(message)
except:
pass

def handle_client(self, client_socket, nickname):
while True:
try:
message = client_socket.recv(1024).decode('utf-8')
if message:
print(f"{nickname}: {message}")
self.broadcast(f"{nickname}: {message}".encode('utf-8'))
else:
self.remove_client(client_socket, nickname)
break
except:
self.remove_client(client_socket, nickname)
break

def remove_client(self, client_socket, nickname):
if client_socket in self.clients:
index = self.clients.index(client_socket)
self.clients.remove(client_socket)
self.nicknames.remove(nickname)
client_socket.close()
self.broadcast(f"{nickname} покинул чат.".encode('utf-8'))
print(f"{nickname} отключился")

if __name__ == "__main__":
server = ChatServer()
server.start()

Запуск

Сначала запустите сервер:

python server.py

Затем запустите два клиента:

python client_gui.py

В появившемся окне введите никнейм, и пользуйтесь чатом.


Простой HTTP-сервер и клиент

Python позволяет быстро развернуть веб-сервер и отправить запрос к нему, используя стандартные библиотеки.

Сервер

from http.server import BaseHTTPRequestHandler, HTTPServer

class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain; charset=utf-8')
self.end_headers()
message = "Привет! Это простой сервер на Python."
self.wfile.write(message.encode('utf-8'))

def run(server_class=HTTPServer, handler_class=MyHandler, port=8000):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
print(f"Сервер запущен на порту {port}")
httpd.serve_forever()

if __name__ == "__main__":
run()

Клиент

import urllib.request

def make_request(url):
try:
response = urllib.request.urlopen(url)
Данные = response.read().decode('utf-8')
print(f"Статус: {response.status}")
print(f"Ответ сервера:\n{Данные}")
except Exception as e:
print(f"Ошибка при подключении: {e}")

if __name__ == "__main__":
url = "http://localhost:8000"
make_request(url)

Разбор:

  • BaseHTTPRequestHandler: Базовый класс для обработки HTTP-запросов. Метод do_GET реагирует на запросы типа GET.
  • send_response, send_header, end_headers: Последовательность методов для формирования заголовков ответа.
  • urllib.request: Библиотека для выполнения HTTP-запросов. Функция urlopen открывает соединение с URL.
  • Кодировка: Метод encode преобразует текст в байты для отправки, decode — для чтения.

Отправитель HTTP-запросов

Расширенный пример клиента с возможностью отправки данных методом POST.

import urllib.request
import urllib.parse

def send_post_request(url, data_dict):
# Преобразование словаря в строку параметров (формат application/x-www-form-urlencoded)
Данные = urllib.parse.urlencode(data_dict).encode('utf-8')

request = urllib.request.Request(url, Данные=Данные, method='POST')
request.add_header('Content-Type', 'application/x-www-form-urlencoded')

try:
with urllib.request.urlopen(request) as response:
result = response.read().decode('utf-8')
print(f"Запрос отправлен. Ответ: {result}")
except urllib.error.HTTPError as e:
print(f"Ошибка HTTP: {e.code} - {e.reason}")
except Exception as e:
print(f"Ошибка сети: {e}")

if __name__ == "__main__":
target_url = "https://httpbin.org/post"
payload = {
"username": "test_user",
"message": "Hello from Python script"
}
send_post_request(target_url, payload)

Разбор:

  • urlencode: Преобразует словарь параметров в строку вида key1=value1&key2=value2.
  • Класс Request: Позволяет задать метод запроса (POST) и дополнительные заголовки.
  • Контекстный менеджер with: Автоматически закрывает соединение после получения ответа.

Утилита для сканирования директорий

Инструмент для обхода дерева папок и вывода информации о файлах.

import os

def scan_directory(path, depth=0):
max_depth = 3 # Ограничение глубины для наглядности

if depth > max_depth:
return

indent = " " * depth
items = os.listdir(path)

for item in sorted(items):
full_path = os.path.join(path, item)
if os.path.isdir(full_path):
print(f"{indent}[DIR] {item}/")
scan_directory(full_path, depth + 1)
else:
size = os.path.getsize(full_path)
print(f"{indent}[FILE] {item} ({size} байт)")

if __name__ == "__main__":
directory = "."
scan_directory(directory)

Разбор:

  • Рекурсия: Функция вызывает сама себя для обработки вложенных подпапок.
  • os.listdir: Возвращает список имен файлов и папок в указанной директории.
  • os.path.join: Корректно формирует путь к файлу независимо от операционной системы.
  • os.path.isdir: Проверяет, является ли путь директорией.
  • os.path.getsize: Получает размер файла в байтах.

Скрипт для создания резервного копирования файлов

Автоматическое создание копии файлов с добавлением временной метки.

import os
import shutil
from datetime import datetime

def create_backup(source_dir, backup_dir):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_name = f"backup_{timestamp}"
backup_path = os.path.join(backup_dir, backup_name)

if not os.path.exists(backup_dir):
os.makedirs(backup_dir)

try:
shutil.copytree(source_dir, backup_path)
print(f"Резервная копия создана: {backup_path}")
except FileExistsError:
print("Ошибка: Резервная копия с таким именем уже существует.")
except Exception as e:
print(f"Ошибка копирования: {e}")

if __name__ == "__main__":
source = "./my_project"
destination = "./backups"
create_backup(source, destination)

Разбор:

  • datetime: Генерирует уникальное имя папки на основе текущего времени.
  • shutil.copytree: Копирует всю директорию вместе со всеми вложенными файлами и поддиректориями.
  • os.makedirs: Создает целевую директорию, если она отсутствует.

Мониторинг дискового пространства

Использование модуля psutil (часто используется в таких задачах) или стандартных средств ОС для проверки свободного места. В данном примере показано использование встроенного модуля shutil для Unix/Linux и Windows.

import shutil
import os

def check_disk_usage(path='/'):
total, used, free = shutil.disk_usage(path)

percent_free = (free / total) * 100

print(f"\nДиск: {path}")
print(f"Всего: {total // (2**30)} GB")
print(f"Занято: {used // (2**30)} GB")
print(f"Свободно: {free // (2**30)} GB")
print(f"Свободно (%): {percent_free:.2f}%")

if percent_free < 20:
print("Внимание: Свободного места менее 20%!")

if __name__ == "__main__":
root_path = "/" if os.name != 'nt' else "C:\\"
check_disk_usage(root_path)

Разбор:

  • shutil.disk_usage: Возвращает кортеж из общего размера, занятого и свободного пространства в байтах.
  • Битовые сдвиги (// 2**30): Перевод байтов в гигабайты.
  • Определение ОС: Проверка os.name позволяет выбрать правильный корневой путь для Windows или Linux.

Парсер URL и проверка доступности ресурса

Разбор структуры ссылки и проверка её работоспособности.

from urllib.parse import urlparse
import urllib.request

def analyze_and_check(url):
parsed = urlparse(url)

print(f"URL: {url}")
print(f"Схема: {parsed.scheme}")
print(f"Домен: {parsed.netloc}")
print(f"Путь: {parsed.path}")
print(f"Параметры: {parsed.query}")

try:
response = urllib.request.urlopen(url, timeout=5)
print(f"Статус доступности: OK ({response.status})")
except urllib.error.URLError as e:
print(f"Ошибка доступа: {e.reason}")
except Exception as e:
print(f"Неизвестная ошибка: {e}")

if __name__ == "__main__":
test_url = "https://www.example.com"
analyze_and_check(test_url)

Разбор:

  • urlparse: Разбивает строку URL на составные части (схема, хост, путь, параметры).
  • timeout: Параметр ограничивает время ожидания ответа, чтобы скрипт не зависал.
  • Обработка URLError: Специфичная обработка ошибок сетевого взаимодействия.

Конвертер форматов дат

Преобразование строк с датами между различными форматами.

from datetime import datetime

def convert_date(date_string, current_format, target_format):
try:
# Парсинг входной строки в объект datetime
date_obj = datetime.strptime(date_string, current_format)

# Форматирование объекта в целевой формат
result = date_obj.strftime(target_format)

return result
except ValueError:
return "Ошибка: Неправильный формат входной даты."

if __name__ == "__main__":
input_str = "2026-05-06"
fmt_in = "%Y-%m-%d"
fmt_out = "%d.%m.%Y %A"

converted = convert_date(input_str, fmt_in, fmt_out)
print(f"Вход: {input_str} -> Выход: {converted}")

Разбор:

  • strptime: Преобразует строку в объект даты и времени согласно шаблону.
  • strftime: Преобразует объект даты и времени обратно в строку по шаблону.
  • Коды форматов: %Y (год), %m (месяц), %d (день), %A (полное название дня недели).

Утилита для просмотра запущенных процессов

Просмотр списка активных процессов и их идентификаторов.

import subprocess
import platform

def list_processes():
Система = platform.Система()
command = []

if Система == "Windows":
command = ["tasklist"]
else:
command = ["ps", "-ef"]

try:
result = subprocess.run(command, capture_output=True, text=True, check=True)
print(f"Система: {Система}\n")
print(result.stdout[:1000]) # Ограничение вывода для краткости
except subprocess.CalledProcessError as e:
print(f"Ошибка выполнения команды: {e}")
except Exception as e:
print(f"Ошибка: {e}")

if __name__ == "__main__":
list_processes()

Разбор:

  • subprocess.run: Выполняет внешнюю команду операционной системы.
  • capture_output=True: Перехватывает вывод команды вместо вывода в консоль.
  • text=True: Возвращает результат как строку, а не как байты.
  • platform.Система: Определяет текущую операционную систему для выбора правильной команды.

Характерный пример для Python: Использование декораторов

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

import time

def timer_decorator(func):
"""Декоратор для измерения времени выполнения функции"""
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
duration = end_time - start_time
print(f"Функция {func.__name__} выполнилась за {duration:.4f} сек.")
return result
return wrapper

@timer_decorator
def slow_function(n):
time.sleep(n)
return n * 2

if __name__ == "__main__":
slow_function(2)

Разбор:

  • Высокоуровневые функции: Функция timer_decorator принимает другую функцию как аргумент.
  • Замыкания (Closure): Внутренняя функция wrapper запоминает контекст внешней функции.
  • Атрибут *args и **kwargs: Позволяют декоратору принимать любое количество позиционных и именованных аргументов, передавая их оригинальной функции.
  • Синтаксический сахар (@): Запись @timer_decorator над функцией является сокращением для вызова slow_function = timer_decorator(slow_function).

См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).