Для генератора паролей используйте модуль secrets, а не random. Он получает случайность из источника операционной системы, предназначенного для секретов. Наша программа создаст пароль заданной длины, гарантирует буквы и цифру, по желанию добавит специальные символы и скопирует результат в буфер обмена. Интерфейс соберём на встроенном Tkinter.
Генератор паролей выглядит как упражнение на одну строку, но у него есть важное отличие от обычной игры в числа. Результат защищает реальный аккаунт, поэтому предсказуемая псевдослучайность не подходит. Кроме того, пользователь должен видеть, какие группы символов включены, не получить слишком короткий результат и не потерять пароль при копировании. Мы разделим чистую функцию генерации и графический интерфейс, чтобы каждую часть можно было проверять отдельно.
Собираем допустимые группы: строчные, заглавные, цифры и символы.
Берём минимум по одному знаку из каждой группы и заполняем остаток через secrets.
Tkinter показывает результат и переносит его в системный буфер обмена.
Почему secrets подходит, а random нет
Модуль random полезен для моделирования, игр и случайного порядка, но его генератор не предназначен для криптографических секретов. Официальная документация Python прямо рекомендует secrets для паролей, токенов и ссылок сброса. Мы используем secrets.choice() для выбора символов и SystemRandom().shuffle() для перемешивания обязательных знаков с остальными.
| Инструмент | Подходит для пароля | Пример задачи |
|---|---|---|
random.choice | Нет | Игра, симуляция, учебная выборка |
secrets.choice | Да | Выбор непредсказуемого символа |
secrets.token_urlsafe | Да, для токена | Ссылка подтверждения или API-токен |
| Собственный алгоритм от времени | Нет | Легко предсказать и повторить |

Настройки формируют группы символов, secrets выбирает значения, а перемешивание скрывает обязательные позиции.
Пишем функцию и окно Tkinter
Создайте файл password_generator.py. Чистая функция принимает длину и флаг символов, формирует список групп и сначала берёт по одному знаку из каждой. Затем она заполняет остаток общим алфавитом и перемешивает список. Интерфейс только читает настройки, вызывает функцию и работает с буфером обмена.
import secrets
import string
import tkinter as tk
from tkinter import messagebox, ttk
SYMBOLS = "!@#$%^&*_-+="
def generate_password(length: int, use_symbols: bool = True) -> str:
pools = [string.ascii_lowercase, string.ascii_uppercase, string.digits]
if use_symbols:
pools.append(SYMBOLS)
if length < max(8, len(pools)):
raise ValueError("Длина должна быть не меньше 8")
chars = [secrets.choice(pool) for pool in pools]
alphabet = "".join(pools)
chars.extend(secrets.choice(alphabet) for _ in range(length - len(chars)))
secrets.SystemRandom().shuffle(chars)
return "".join(chars)
def build_app() -> tk.Tk:
root = tk.Tk()
root.title("Генератор паролей")
root.resizable(False, False)
length = tk.IntVar(value=16)
use_symbols = tk.BooleanVar(value=True)
password = tk.StringVar()
status = tk.StringVar(value="Выберите параметры и нажмите «Создать»")
frame = ttk.Frame(root, padding=20)
frame.grid()
ttk.Label(frame, text="Длина").grid(row=0, column=0, sticky="w")
ttk.Spinbox(frame, from_=8, to=64, textvariable=length, width=7).grid(row=0, column=1)
ttk.Checkbutton(frame, text="Добавить символы", variable=use_symbols).grid(
row=1, column=0, columnspan=2, sticky="w", pady=10
)
ttk.Entry(frame, textvariable=password, width=34, state="readonly").grid(
row=2, column=0, columnspan=2, pady=8
)
def refresh() -> None:
try:
password.set(generate_password(length.get(), use_symbols.get()))
status.set(f"Создан пароль длиной {length.get()} символов")
except (ValueError, tk.TclError) as error:
messagebox.showerror("Проверьте длину", str(error))
def copy_password() -> None:
if not password.get():
status.set("Сначала создайте пароль")
return
root.clipboard_clear()
root.clipboard_append(password.get())
root.update()
status.set("Пароль скопирован")
ttk.Button(frame, text="Создать", command=refresh).grid(row=3, column=0, sticky="ew")
ttk.Button(frame, text="Копировать", command=copy_password).grid(row=3, column=1, sticky="ew")
ttk.Label(frame, textvariable=status).grid(row=4, column=0, columnspan=2, pady=(12, 0))
return root
if __name__ == "__main__":
app = build_app()
app.mainloop()- Результат имеет ровно ту длину, которую передали в
length. - В нём есть хотя бы одна строчная буква, одна заглавная и одна цифра.
- При включённом флаге присутствует хотя бы один символ из безопасно заданного набора.
- Обязательные знаки перемешаны и не стоят постоянно в начале в одном порядке.

Оба модуля выбирают элементы, но только secrets предназначен для данных, которые должны быть непредсказуемыми.
Как устроены длина, группы и копирование
Чем больше алфавит и длина, тем больше возможных комбинаций, но универсального числа для всех сервисов нет. Для нашего учебного интерфейса минимум равен восьми, значение по умолчанию равно шестнадцати, а максимум в поле равен шестидесяти четырём. Это ограничения программы, а не обещание соответствия любой политике безопасности. Кнопка копирования очищает буфер, добавляет текущую строку и вызывает обновление Tkinter.
| Элемент | Значение в примере | Что меняет |
|---|---|---|
| Длина | 8-64, по умолчанию 16 | Количество позиций в результате |
| Строчные буквы | Всегда включены | Хотя бы один знак a-z |
| Заглавные буквы | Всегда включены | Хотя бы один знак A-Z |
| Цифры | Всегда включены | Хотя бы один знак 0-9 |
| Спецсимволы | Переключатель | Добавляет заданный набор |
| Буфер обмена | Кнопка Копировать | Передаёт строку другому приложению |
Практика: проверяем генератор, а не одну удачную строку
Случайный результат нельзя проверять точным сравнением. Проверяйте свойства: длину, наличие групп и разнообразие серии. Временно добавьте небольшой тестовый фрагмент под защитой if __name__ == "__main__" или вынесите его в отдельный файл. Не печатайте реальные пароли пользователя в журналы и не сохраняйте их автоматически.
- Запустите
python password_generator.pyи создайте пароль длиной 16 с символами. - Посчитайте символы вручную или через
len(): результат должен иметь длину 16. - Повторите генерацию десять раз. Строки должны различаться, а окно не должно зависать.
- Выключите символы и убедитесь, что результат состоит только из букв и цифр.
- Введите значение меньше восьми. Программа должна показать понятное окно ошибки, а не traceback.
- Нажмите «Копировать», вставьте результат во временное поле и сравните строки.
- Импортируйте функцию из другого файла: окно не должно открываться благодаря проверке
__name__.
- Каждый запуск функции возвращает строку нужной длины с обязательными группами символов.
- Некорректная длина обрабатывается внутри интерфейса и объясняется пользователю.
- Кнопки создания и копирования работают несколько раз подряд.
- Генератор не записывает созданные значения в файл, консоль или историю приложения.

Случайную строку проверяют не на точное значение, а на свойства и безопасное обращение.
Какие ошибки делают генератор небезопасным или неудобным
Красивое окно не исправляет слабый источник случайности. Сначала проверьте модуль и алгоритм, затем интерфейс. Второй частый перекос состоит в попытке гарантировать группы повторной генерацией всей строки: такой цикл сложнее читать и может скрывать проблему с короткой длиной. Явно добавленные обязательные символы делают правило видимым, а перемешивание убирает постоянные позиции.
Он предназначен для моделирования, а не секретов. Замените выбор на функции модуля secrets.
Алфавит слишком мал для той же длины. Включите несколько групп и проверяйте ограничения конкретного сервиса.
Сначала выбранные знаки нужно перемешать с заполнением, иначе структура становится предсказуемой.
Журналы и история терминала не должны становиться вторым хранилищем секретов.
Поместите запуск GUI под if __name__ == "__main__", чтобы функцию можно было тестировать отдельно.
Почему нельзя использовать random?
Его генератор не предназначен для криптографических секретов. Для паролей Python рекомендует secrets.
Как гарантировать наличие цифры?
Сначала явно выбрать один символ из string.digits, затем добавить остальное и перемешать список.
Зачем нужен shuffle?
Без перемешивания обязательные буква, цифра и символ всегда занимали бы известные позиции.
Почему окно не создают на уровне импорта?
Защита if __name__ == "__main__" позволяет импортировать и тестировать функцию без запуска GUI.
Безопасно ли хранить пароль в буфере?
Буфер удобен, но не является защищённым долговременным хранилищем. После вставки учитывайте историю буфера устройства.
Функции, списки, исключения и модули можно закрепить в курсе Python. Если программа падает на вводе, откройте разбор строк и чисел, а окружение для проекта настройте по гайду виртуальных окружений.
Для реального использования прочитайте памятку о паролях и 2FA. Следующий проект можно построить по тому же принципу: чистая функция отдельно, интерфейс отдельно, проверка свойств отдельно. Маршрут по базе языка есть во введении в Python для новичков.