Разбить программу на части, которые можно назвать, проверить и использовать снова
Функция — именованный блок кода. def объявляет её: имя, в скобках параметры, после двоеточия — тело с отступом. Объявление ничего не выполняет; тело работает при каждом вызове имя(аргументы). return завершает функцию и отдаёт значение туда, откуда её вызвали.
>>> def greet(name):
... return f"Здравствуйте, {name}!"
...
>>> greet("Анна")
'Здравствуйте, Анна!'
>>> message = greet("Борис")
>>> message.upper()
'ЗДРАВСТВУЙТЕ, БОРИС!'
>>> def say(name):
... print(f"Здравствуйте, {name}!")
...
>>> result = say("Вера")
Здравствуйте, Вера!
>>> print(result)
None
У greet и say один и тот же видимый эффект в интерактивном режиме, но разное поведение. greet возвращает строку — её можно сохранить, передать дальше, преобразовать. say только печатает; функция без return возвращает None. Правило: функция, которая считает, возвращает результат, а печатает вызывающий код. Тогда её можно использовать и в программе с окнами, и в веб-сервисе, и в тесте.
Параметр может иметь значение по умолчанию — тогда аргумент можно не передавать. Аргументы передаются по позиции или по имени: rate=0. Именованные аргументы делают вызов понятным без заглядывания в объявление и позволяют менять порядок.
>>> def price_with_vat(amount, rate=20):
... return round(amount * (1 + rate / 100), 2)
...
>>> price_with_vat(1000)
1200.0
>>> price_with_vat(1000, 10)
1100.0
>>> price_with_vat(rate=0, amount=1000)
1000.0
>>> price_with_vat()
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
price_with_vat()
~~~~~~~~~~~~~~^^
TypeError: price_with_vat() missing 1 required positional argument: 'amount'
>>> def total(*numbers):
... return sum(numbers)
...
>>> total(1, 2, 3, 4)
10
>>> def min_max(values):
... return min(values), max(values)
...
>>> low, high = min_max([7, 2, 9, 4])
>>> low, high
(2, 9)
Параметры без умолчания пишутся первыми. *numbers собирает любое число позиционных аргументов в кортеж. Чтобы вернуть несколько значений, функция возвращает кортеж, а вызывающий распаковывает его.
Значение по умолчанию вычисляется один раз — при объявлении функции, а не при каждом вызове. Если это список, все вызовы делят один и тот же список:
>>> def add_item(item, items=[]):
... items.append(item)
... return items
...
>>> add_item("хлеб")
['хлеб']
>>> add_item("молоко")
['хлеб', 'молоко']
>>> def add_item(item, items=None):
... if items is None:
... items = []
... items.append(item)
... return items
...
>>> add_item("хлеб")
['хлеб']
>>> add_item("молоко")
['молоко']
Второй вызов вернул список с «хлебом» из первого. Правильный приём — умолчание None и создание нового списка внутри функции. is None — проверка «это именно None», так её принято записывать. С числами, строками и None в умолчаниях проблем нет: они неизменяемы.
Переменные, созданные внутри функции (и её параметры), — локальные: они существуют, пока функция выполняется, и не видны снаружи. Глобальные переменные (объявленные в файле вне функций) функция читать может, а присваивание внутри функции создаёт локальную переменную с тем же именем:
>>> count = 0
>>> def show():
... print("Вижу count =", count)
...
>>> show()
Вижу count = 0
>>> def increment():
... count = count + 1
...
>>> increment()
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
increment()
~~~~~~~~~^^
File "<python-input-3>", line 2, in increment
count = count + 1
^^^^^
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value
>>> def increment():
... global count
... count += 1
...
>>> increment()
>>> count
1
В increment строка count = count + 1 делает count локальным для всей функции, а прочитать локальную переменную до присваивания нельзя. global count разрешает менять глобальную — но так почти не пишут: функция, которая тайком меняет внешние переменные, непредсказуема. Нормальный путь — передать значение параметром и вернуть результат: count = increment(count).
Строка в тройных кавычках сразу после def — строка документации (docstring): что делает функция и что означают параметры. Её показывают help и подсказки редактора. Аннотации типов weight: float и -> float сообщают, каких типов ждут параметры и что вернётся.
>>> def bmi(weight: float, height: float) -> float:
... """Индекс массы тела: вес в кг, рост в метрах."""
... return weight / height ** 2
...
>>> round(bmi(70, 1.75), 1)
22.9
>>> help(bmi)
Help on function bmi in module __main__:
bmi(weight: float, height: float) -> float
Индекс массы тела: вес в кг, рост в метрах.
Интерпретатор аннотации не проверяет: bmi("70", 1.75) всё равно упадёт с TypeError только при вычислении. Их читают человек, редактор (VS Code подчеркнёт неверный тип) и программы проверки вроде mypy. В учебных программах аннотации необязательны, в рабочем коде — общепринятая практика.
Модуль — любой файл .py. Функции, вынесенные в отдельный файл, подключаются в другие программы командой import. Модуль с функциями для текста:
"""Функции для обработки текста."""
def normalize_spaces(text: str) -> str:
"""Убирает лишние пробелы: по краям и повторы внутри."""
return " ".join(text.split())
def count_words(text: str) -> int:
"""Число слов, разделённых пробелами."""
return len(text.split())
def truncate(text: str, limit: int = 20, tail: str = "…") -> str:
"""Обрезает текст до limit символов, добавляя tail."""
if len(text) <= limit:
return text
return text[:limit - len(tail)] + tail
if __name__ == "__main__":
# Самопроверка: выполняется только при запуске файла, не при импорте
assert normalize_spaces(" а б ") == "а б"
assert count_words("раз два три") == 3
assert truncate("короткий") == "короткий"
assert len(truncate("очень длинная строка для проверки")) == 20
print("textutils: все проверки пройдены")
Блок if __name__ == "__main__": выполняется, только когда файл запущен как программа. При импорте переменная __name__ равна имени модуля, и блок пропускается. Здесь в нём самопроверка: assert условие молча проходит, если условие истинно, и останавливает программу с AssertionError, если ложно.
$ python3 textutils.py
textutils: все проверки пройдены
Программа, которая использует модуль, лежит в том же каталоге:
import textutils
from textutils import truncate
titles = [
" Основы Python ",
"Как устроены словари и множества в Python",
"Функции",
]
for t in titles:
clean = textutils.normalize_spaces(t)
print(f"{truncate(clean, 30):30} | слов: {textutils.count_words(clean)}")
$ python3 report.py
Основы Python | слов: 2
Как устроены словари и множес… | слов: 7
Функции | слов: 1
import textutils — подключить модуль целиком, обращаться через точку: textutils.count_words. from textutils import truncate — взять одно имя, обращаться без префикса. Первая форма нагляднее показывает, откуда функция; вторую используют для часто вызываемых имён. Импорт from модуль import * не пишут: непонятно, какие имена появились в программе.
random.py, math.py, string.py, json.py. Python ищет модули сначала в каталоге программы, и import random подключит ваш файл вместо настоящего — с непонятной ошибкой AttributeError.Вместе с Python ставятся сотни модулей — «батарейки в комплекте». Импорт можно сократить псевдонимом: import datetime as dt.
>>> import math
>>> math.sqrt(2)
1.4142135623730951
>>> math.ceil(7 / 2), math.floor(7 / 2)
(4, 3)
>>> math.pi
3.141592653589793
>>> from math import hypot
>>> hypot(3, 4)
5.0
>>> import random
>>> random.choice(["орёл", "решка"])
'орёл'
>>> deck = list(range(1, 11))
>>> random.shuffle(deck)
>>> deck
[1, 7, 3, 4, 10, 5, 8, 2, 9, 6]
>>> from datetime import date
>>> start = date(2026, 9, 1)
>>> end = date(2026, 12, 31)
>>> (end - start).days
121
>>> start.strftime("%d.%m.%Y")
'01.09.2026'
>>> start.weekday()
1
| Модуль | Для чего |
|---|---|
math | Корни, округление вверх и вниз, тригонометрия, константы |
random | Случайные числа для игр, моделирования, перемешивания |
secrets | Случайные значения для паролей и токенов |
datetime | Даты и время: разница, формат, день недели (weekday(): 0 — понедельник) |
string | Готовые наборы символов: буквы, цифры, знаки |
pathlib, os, shutil | Файлы и каталоги (модули 07, 08) |
csv, json | Табличные и структурированные данные (модуль 07) |
Результаты random в вашем запуске будут другими. Генератор random предсказуем: зная несколько выданных чисел, можно вычислить следующие. Для паролей, кодов подтверждения и токенов используют secrets:
import secrets
import string
ALPHABET = string.ascii_letters + string.digits
def make_password(length: int = 12) -> str:
"""Случайный пароль из латинских букв и цифр."""
return "".join(secrets.choice(ALPHABET) for _ in range(length))
for _ in range(3):
print(make_password())
print(make_password(20))
$ python3 password.py
jNl304IKjrw7
YQom48rFqv7Q
ntPa87GfN4kb
2DL4aaU7R63eswD2gQV5
Пароли в вашем запуске будут другими; пароли из примера не используйте — они опубликованы. secrets.choice(ALPHABET) for _ in range(length) — выражение-генератор из модуля 05; _ — принятое имя для переменной, значение которой не нужно.
Программа больше одного экрана строится так: вверху импорты и константы, затем функции, внизу — функция main, которая их вызывает, и запуск под if __name__ == "__main__". Каждая функция делает одно дело и получает всё нужное через параметры.
"""Учёт оценок: ввод, расчёт, отчёт."""
import statistics
PASS_SCORE = 55
def read_scores() -> list[int]:
"""Читает баллы до пустой строки."""
scores = []
while line := input("Баллы (Enter — конец): "):
scores.append(int(line))
return scores
def summary(scores: list[int]) -> dict:
"""Среднее, медиана и число не сдавших."""
return {
"среднее": statistics.mean(scores),
"медиана": statistics.median(scores),
"не сдали": sum(1 for s in scores if s < PASS_SCORE),
}
def main():
scores = read_scores()
if not scores:
print("Нет данных")
return
for name, value in summary(scores).items():
print(f"{name}: {value}")
if __name__ == "__main__":
main()
$ python3 scores_app.py
Баллы (Enter — конец): 78
Баллы (Enter — конец): 92
Баллы (Enter — конец): 45
Баллы (Enter — конец): 61
Баллы (Enter — конец): 88
Баллы (Enter — конец): 50
Баллы (Enter — конец):
среднее: 69
медиана: 69.5
не сдали: 2
Такую программу легко проверять: summary можно вызвать из теста со списком чисел, не вводя их руками. := («морж») присваивает и сразу возвращает значение — цикл читает строку и проверяет, что она не пустая, в одном условии. Две пустые строки между функциями — требование PEP 8.
return; без return возвращается NoneNoneglobal.py; код запуска — под if __name__ == "__main__"secrets, не random; свои файлы не называют именами стандартных модулей