Когда код становится длиннее и задачи усложняются, без инструментов для упорядочивания логики уже не обойтись. В Python таким инструментом служат функции.
Что такое функции в Python и для чего они нужны
Функции — это способ собрать блок кода в единое целое и дать ему имя. Они позволяют вынести повторяющиеся или логически связанные действия в отдельный элемент программы, который можно вызывать по мере необходимости.
Зачем они нужны:
- Чтобы не дублировать код. Если одна и та же операция встречается в программе много раз, проще вынести ее в функцию и просто вызывать ее по имени.
- Чтобы упростить чтение кода. Программу легче понимать, когда она разбита на логичные части с понятными названиями.
- Чтобы легче вносить изменения. Исправляя функцию, вы автоматически обновляете все места, где она используется.
- Чтобы использовать модульный подход. Функции помогают разрабатывать большие проекты по частям, а затем собирать их в целое.
В итоге функции — это основа удобного и чистого кода. Они помогают разделить сложные задачи на простые шаги и выстраивать программу так, чтобы ее логика была понятной не только автору, но и любому другому разработчику.
Синтаксис функций
В Python для создания функций предусмотрены свои правила, и именно они позволяют интерпретатору понять, где начинается и где заканчивается блок кода. Если нарушить эти правила, программа просто не запустится.
Функция объявляется с помощью ключевого слова def (от англ. define — «определить»). После него идет имя функции, круглые скобки для аргументов и двоеточие. Все, что относится к работе функции, пишется в ее теле — с обязательным отступом. В конце можно вернуть результат с помощью ключевого слова return.
Общая структура функции выглядит так:
def имя_функции(аргументы):
тело_функции
return результат
Где:
имя_функции — это название, по которому функция вызывается из других частей программы;
аргументы — данные, которые функция получает на вход. Скобки могут быть пустыми, если входные данные не нужны;
тело_функции — последовательность инструкций, которая выполняется при вызове;
результат — возвращаемое значение, которое можно использовать дальше в программе.
Виды функций в Python
Функции в Python можно разделить на несколько категорий в зависимости от того, как они создаются, используются и какую роль выполняют в программе:
Встроенные функции
Python поставляется с набором встроенных функций, которые доступны сразу после запуска интерпретатора. Их не нужно импортировать из модулей — они работают из коробки.
Всего таких функций 71, и они охватывают практически все базовые потребности разработчика. С их помощью можно:
- преобразовывать данные из одного типа в другой;
- получать информацию о значениях и объектах;
- выполнять вычисления;
- управлять коллекциями;
- работать с функциями и объектами.
Благодаря встроенным функциям можно быстро и просто выполнять рутинные операции, не изобретая велосипед.
Полный список встроенных функций Python:
| Функция | Описание |
|---|---|
| abs() | Возвращает абсолютное значение числа (модуль) |
| aiter() | Возвращает асинхронный итератор для объекта |
| all() | Возвращает True, если в последовательности нет ложных значений (или если она пустая) |
| anext() | Получает следующий элемент из асинхронного итератора (аналог next() для обычного итератора) |
| any() | Возвращает True, если есть хотя бы одно истинное значение (или False, если все ложные) |
| ascii() | Возвращает строку с представлением объекта, заменяя не-ASCII символы на escape-последовательности |
| bin() | Преобразует целое число в строку с двоичным представлением |
| bool() | Преобразует объект в True или False |
| breakpoint() | Устанавливает точку остановки (запускает отладчик) |
| bytearray() | Создает изменяемый массив байтов |
| bytes() | Создает неизменяемый объект байтов |
| callable() | Проверяет, можно ли вызвать объект (функция ли это и т. д.) |
| chr() | Возвращает символ по его числовому коду (например, chr(65) == 'A') |
| classmethod() | Преобразует метод класса в метод, который получает сам класс в качестве первого аргумента |
| compile() | Компилирует строку или AST в объект кода, готовый к исполнению |
| complex() | Создает комплексное число из двух чисел (действительной и мнимой частей) |
| delattr() | Удаляет указанный атрибут у объекта |
| dict() | Создает словарь (пустой или из набора пар «ключ: значение») |
| dir() | Возвращает список имен атрибутов и методов объекта |
| divmod() | Возвращает кортеж (частное, остаток) от целочисленного деления |
| enumerate() | Возвращает итератор пар (индекс, значение) для итерируемого |
| eval() | Вычисляет строку как выражение Python и возвращает результат |
| exec() | Выполняет динамически сформированный код Python |
| filter() | Фильтрует элементы итерируемого по функции-предикату, возвращая только те, для которых функция вернула True |
| float() | Преобразует значение в число с плавающей точкой |
| format() | Форматирует значение согласно спецификации (аналог f-строк или метода .format()) |
| frozenset() | Создает неизменяемое множество (его нельзя изменить после создания) |
| getattr() | Получает значение атрибута объекта по имени (строке) |
| globals() | Возвращает словарь текущего глобального пространства имен |
| hasattr() | Проверяет, существует ли у объекта атрибут с указанным именем |
| hash() | Возвращает хеш-значение объекта (для использования в словарях и множествах) |
| help() | Выводит справочную информацию по объекту или модулю |
| hex() | Преобразует целое число в строку с шестнадцатеричным представлением |
| id() | Возвращает уникальный идентификатор объекта в текущем сеансе Python |
| input() | Считывает строку из стандартного ввода (обычно — консоли) |
| int() | Преобразует значение в целое число |
| isinstance() | Проверяет, является ли объект экземпляром указанного класса или кортежа классов |
| issubclass() | Проверяет, является ли один класс подклассом другого |
| iter() | Возвращает итератор для указанного итерируемого объекта |
| len() | Возвращает число элементов в объекте (длину последовательности, количество ключей в словаре и т. д.) |
| list() | Создает список (пустой или из итерируемого) |
| locals() | Возвращает словарь текущего локального пространства имен |
| map() | Применяет функцию к каждому элементу итерируемого и возвращает итератор результатов |
| max() | Возвращает наибольший элемент итерируемого или наибольшее из переданных аргументов |
| memoryview() | Создает «вид» на объект байтов без копирования данных |
| min() | Возвращает наименьший элемент итерируемого или наименьшее из переданных аргументов |
| next() | Возвращает следующий элемент итератора, вызывает StopIteration, если элементов больше нет |
| object() | Создает базовый, самый «пустой» объект в Python |
| oct() | Преобразует целое число в строку с восьмеричным представлением |
| open() | Открывает файл и возвращает файловый объект для чтения/записи |
| ord() | Возвращает числовой код символа (аналог chr()) |
| pow() | Возводит число в степень (аналог x**y), может принимать третий аргумент — модуль |
| print() | Выводит переданные значения в стандартный поток вывода (консоль) |
| property() | Создает свойство для управления доступом к атрибутам класса |
| range() | Возвращает итерируемую последовательность чисел по заданному диапазону |
| repr() | Возвращает строковое представление объекта, удобное для разработчика (часто может быть введено обратно в Python) |
| reversed() | Возвращает итератор элементов итерируемого в обратном порядке |
| round() | Округляет число до заданного количества знаков после запятой |
| set() | Создает множество (уникальный набор элементов) |
| setattr() | Устанавливает или изменяет атрибут объекта по имени |
| slice() | Создает объект среза, который можно применять к последовательностям |
| sorted() | Возвращает новый отсортированный список из элементов итерируемого |
| staticmethod() | Превращает метод класса в статический — не принимает ни self, ни cls |
| str() | Преобразует объект в строку |
| sum() | Возвращает сумму элементов итерируемого (начальное значение можно задать вторым аргументом) |
| super() | Позволяет обращаться к методам родительского класса из подкласса |
| tuple() | Создает кортеж (неизменяемую последовательность) |
| type() | Возвращает тип объекта или создает новый класс при трех аргументах |
| vars() | Возвращает словарь атрибутов объекта или текущего пространства имен |
| zip() | Объединяет несколько итерируемых, возвращая итератор кортежей из элементов на одинаковых позициях |
| __import__() | Внутренняя функция импорта модуля. Используется интерпретатором, но доступна и для кода |
Пример:
Представим, у нас есть оценки студентов. Нужно узнать, сколько студентов в списке, какая у них максимальная и минимальная оценка, а также как выглядит список, если отсортировать его по возрастанию.
grades = [72, 85, 90, 64, 100, 77]
print("Количество студентов:", len(grades))
print("Максимальная оценка:", max(grades))
print("Минимальная оценка:", min(grades))
print("Сортировка по возрастанию:", sorted(grades))
Результат выполнения:
Количество студентов: 6
Максимальная оценка: 100
Минимальная оценка: 64
Сортировка по возрастанию: [64, 72, 77, 85, 90, 100]
Здесь мы использовали сразу несколько встроенных функций:
- len() показала количество студентов;
- max() вернула наибольшую оценку;
- min() — наименьшую;
- sorted() аккуратно отсортировала список.
Без встроенных функций пришлось бы писать дополнительные циклы и условия. А так все решается быстро и читается максимально просто.
Пользовательские функции
Пользовательские функции — это функции, которые программист создает сам для решения конкретных задач. Они помогают разбить код на логические блоки, сделать его более аккуратным и понятным. Если встроенные функции закрывают универсальные потребности, то пользовательские позволяют адаптировать программу под свои цели.
Функции могут быть простыми и возвращать, например, сумму чисел, а могут содержать десятки строк и работать с файлами, базами данных или веб-запросами.
Пример:
Возьмем тот же список оценок студентов. Допустим, нам часто нужно вычислять средний балл — вместо того чтобы писать формулу каждый раз, проще оформить ее в функцию:
grades = [72, 85, 90, 64, 100, 77]
def average_grade(grades_list):
total = sum(grades_list)
count = len(grades_list)
return total / count
print("Средний балл:", average_grade(grades))
Результат выполнения:
Средний балл: 81.33333333333333
Теперь для любого списка оценок мы можем вызвать average_grade() и получить результат. Если в будущем захочется изменить логику (например, округлять средний балл), правки нужно будет внести только в функцию — и они автоматически применятся везде, где она используется.
Анонимные функции
В некоторых случаях для решения задачи нужна совсем маленькая функция — настолько простая, что ради нее не хочется писать полноценное определение через def. Тогда в Python используют анонимные функции, которые еще называют лямбда-функциями.
Главное отличие лямбда-функции — у нее нет имени (хотя ее можно присвоить переменной) и она записывается в одну строку. Как правило, такие функции используют там, где важен сам факт вычисления, а не повторное обращение к функции по имени.
Анонимные функции удобно применять:
- при сортировке данных по определенному правилу;
- в связке с функциями высшего порядка (map(), filter(), reduce());
- там, где функция нужна всего один раз и нет смысла заводить для нее отдельное имя.
Пример:
У нас есть список студентов с их баллами. Нужно отсортировать их по оценке от большего к меньшему:
students = [
("Анна", 72),
("Борис", 85),
("Виктор", 90),
("Даша", 64),
("Ирина", 100),
("Сергей", 77)
]
# Отберем только студентов, у которых балл 80 и выше
passed = list(filter(lambda x: x[1] >= 80, students))
print("Студенты с результатом 80 и выше:", passed)
Результат выполнения:
Студенты с результатом 80 и выше: [('Борис', 85), ('Виктор', 90), ('Ирина', 100)]
Здесь lambda x: x[1] >= 80 проверяет второй элемент кортежа (оценку), и в итоговый список попадают только те студенты, кто сдал экзамен на 80+.
Рекурсивные функции
Рекурсивными называют функции, которые вызывают сами себя. На первый взгляд это может показаться странным, но на практике рекурсия очень полезна: она позволяет решать задачи, которые можно разбить на повторяющиеся подзадачи.
Функция делит задачу на более маленькую, вызывает сама себя для ее решения и останавливается, когда достигает базового случая. Без базового условия рекурсия уйдет в бесконечный цикл и программа завершится ошибкой.
Рекурсивные функции часто применяются там, где решение строится шаг за шагом и каждый шаг похож на предыдущий:
- обход древовидных структур (например, каталогов на диске);
- обработка вложенных списков;
- математические задачи — факториал, числа Фибоначчи, степенные функции.
Пример:
Допустим, оценки студентов хранятся не в одном списке, а в виде вложенных списков — по группам. Мы хотим найти общую сумму баллов.
grades = [
[72, 85, 90], # Группа 1
[64, 100, 77], # Группа 2
[88, 95] # Группа 3
]
def recursive_sum(data):
total = 0
for item in data:
if isinstance(item, list): # Если внутри список — вызываем функцию снова
total += recursive_sum(item)
else: # Если число — просто прибавляем
total += item
return total
print("Общая сумма баллов:", recursive_sum(grades))
Результат выполнения:
Общая сумма баллов: 671
В этом примере функция проходит по каждому элементу списка. Если встречает число — прибавляет его к сумме. Если встречает еще один список — запускает сама себя, чтобы обработать и его. Так постепенно раскрываются все вложенные уровни, и в итоге мы получаем сумму всех оценок, даже если они лежат в разных подсписках.
Функции высшего порядка
В Python функции можно использовать не только как команды, но и как объекты. Их можно передавать в другие функции, возвращать из функций и хранить в переменных. Функции, которые принимают другие функции в качестве аргументов или возвращают их в качестве результата, называются функциями высшего порядка.
Они полезны, когда нужно описать логику обработки, а не просто данные. Благодаря этому код становится более гибким: мы можем задать разные правила и использовать одну и ту же функцию для разных задач.
Классические примеры функций высшего порядка в Python — map(), filter() и reduce().
Пример:
Как раз здесь и актуальны функции высшего порядка filter() и map().
students = [
("Анна", 72),
("Борис", 85),
("Виктор", 90),
("Даша", 64),
("Ирина", 100),
("Сергей", 77)
]
# filter — оставляем только студентов, у которых балл 80 и выше
passed = list(filter(lambda x: x[1] >= 80, students))
# map — преобразуем оценки в проценты (от 0 до 1)
percentages = list(map(lambda x: (x[0], x[1] / 100), students))
print("Студенты, сдавшие экзамен:", passed)
print("Оценки в процентах:", percentages)
Результат выполнения:
Студенты, сдавшие экзамен: [('Борис', 85), ('Виктор', 90), ('Ирина', 100)]
Оценки в процентах: [('Анна', 0.72), ('Борис', 0.85), ('Виктор', 0.9), ('Даша', 0.64), ('Ирина', 1.0), ('Сергей', 0.77)]
Где:
- filter() оставляет только те элементы списка, для которых функция возвращает True;
- map() применяет функцию ко всем элементам и возвращает новый список.
Асинхронные функции
В обычной программе Python каждая строка кода выполняется по порядку: пока не завершится одна операция, следующая не начнется. Но иногда это неудобно. Например, нужно отправить запрос на сервер за данными или подождать ответа от базы — программа зависает и ничего не делает в это время. Чтобы избежать простоев, в Python и используются асинхронные функции.
Они позволяют запускать задачи так, чтобы во время ожидания программа продолжала работать дальше. Для их создания применяются ключевые слова async и await.
Асинхронность особенно полезна при работе с сетевыми запросами (например, загрузка данных с сайта), в чат-ботах и веб-приложениях и при одновременной обработке нескольких задач, где часть из них ждет ответа извне.
Пример:
Представим, что оценки студентов загружаются не из списка в коде, а с удаленного сервера. Мы напишем асинхронную функцию, которая имитирует такую загрузку.
import asyncio
students = [
("Анна", 72),
("Борис", 85),
("Виктор", 90)
]
async def fetch_grade(student):
print(f"Загружаю данные для {student[0]}...")
await asyncio.sleep(1) # имитация задержки при запросе
print(f"Данные получены: {student[1]}")
return student
async def main():
tasks = [fetch_grade(student) for student in students]
results = await asyncio.gather(*tasks)
print("Все данные загружены:", results)
asyncio.run(main())
Результат выполнения:
Загружаю данные для Анна...
Загружаю данные для Борис...
Загружаю данные для Виктор...
Данные получены: 72
Данные получены: 85
Данные получены: 90
Все данные загружены: [('Анна', 72), ('Борис', 85), ('Виктор', 90)]
Генераторные функции
В Python есть особый тип функций — функции-генераторы. Они работают похоже на обычные, но вместо того чтобы сразу вернуть результат через return, они используют ключевое слово yield.
Отличие генератора в том, что он отдает результат по одному элементу за раз, а не весь список сразу. Он помнит свое состояние между вызовами и продолжает работу с того места, где остановился.
Главное преимущество генераторов — экономия памяти. Они не хранят все данные целиком, а создают их по мере необходимости.
Пример:
Снова представим, что у нас есть список студентов, и мы хотим получать их оценки по одной, а не все сразу.
students = [
("Анна", 72),
("Борис", 85),
("Виктор", 90),
("Даша", 64),
("Ирина", 100)
]
def grade_generator(students_list):
for student in students_list:
yield student
# Используем генератор
for s in grade_generator(students):
print(f"{s[0]} получил(а) {s[1]} баллов")
Результат выполнения:
Анна получил(а) 72 баллов
Борис получил(а) 85 баллов
Виктор получил(а) 90 баллов
Даша получил(а) 64 баллов
Ирина получил(а) 100 баллов
Генераторная функция grade_generator() не возвращает весь список сразу, а выдает студентов по одному. Она позволяет работать с данными последовательно и экономить ресурсы. Если студентов очень много, программа не загружает их все в память разом, а обрабатывает постепенно, по мере запроса.
Аргументы функций в Python
При вызове функции можно передавать данные в виде аргументов. Они делают функции гибкими и позволяют использовать один и тот же код для разных входных значений.
В Python существует несколько типов аргументов:
По умолчанию
В Python параметрам функции можно задавать значения по умолчанию. Это значит, что если при вызове функции аргумент не указан, используется заранее определенное значение. Такой прием упрощает код и делает функции удобнее — не нужно каждый раз передавать одинаковые параметры вручную.
Пример:
def student_info(name, group="Не указана"):
print("Имя:", name)
print("Группа:", group)
student_info("Анна")
student_info("Борис", "ИСТ-21")
Результат выполнения:
Имя: Анна
Группа: Не указана
Имя: Борис
Группа: ИСТ-21
В первом вызове передано только имя, и функция автоматически подставила значение по умолчанию для группы. Во втором случае значение было передано явно, поэтому оно и использовалось.
Именованные аргументы
В Python при вызове функции можно явно указывать, какой параметр чему соответствует — с помощью именованных аргументов. Благодаря этому порядок передачи значений не имеет значения — главное, чтобы имя совпадало с названием параметра в функции.
Пример:
def student(fname, lname):
print("Имя:", fname)
print("Фамилия:", lname)
student(fname="Иван", lname="Петров")
student(lname="Петров", fname="Иван")
Результат выполнения:
Имя: Иван
Фамилия: Петров
Имя: Иван
Фамилия: Петров
Позиционные аргументы
Самый привычный способ передачи данных в функцию — это позиционные аргументы. Значения связываются с параметрами по порядку: первый аргумент идет в первый параметр, второй — во второй и так далее.
Пример:
def student_info(name, age):
print("Имя:", name)
print("Возраст:", age)
print("Вызов 1:")
student_info("Анна", 20)
print("\nВызов 2:")
student_info(20, "Анна")
Результат выполнения:
Вызов 1:
Имя: Анна
Возраст: 20
Вызов 2:
Имя: 20
Возраст: Анна
В первом случае аргументы переданы в правильном порядке. Во втором — значения перепутаны местами, и функция вывела нелогичный результат. Поэтому при использовании позиционных аргументов важно следить за порядком.
Аргументы переменной длины (*args и **kwargs)
Бывают ситуации, когда заранее неизвестно, сколько именно аргументов нужно передать функции. Чтобы не ограничивать себя фиксированным количеством параметров, в Python есть механизм аргументов переменной длины:
- *args собирает все позиционные аргументы в кортеж.
- **kwargs собирает все именованные аргументы в словарь.
Пример:
def show_info(*args, **kwargs):
print("Позиционные аргументы (*args):")
for arg in args:
print(arg)
print("\nИменованные аргументы (**kwargs):")
for key, value in kwargs.items():
print(f"{key} = {value}")
show_info("Анна", "Борис", group="ИСТ-21", course=2)
Результат выполнения:
Позиционные аргументы (*args):
Анна
Борис
Именованные аргументы (**kwargs):
group = ИСТ-21
course = 2
Таким образом, функция принимает любое количество аргументов. Сначала в нее попадают все значения без имени (Анна, Борис), а потом — пары «ключ = значение» (group, course).
Как использовать результат функции в другой функции
В Python функции — это такие же объекты, как числа или строки. Их можно не только вызывать, но и передавать в другие функции, хранить в переменных и даже использовать как аргументы.
Пример:
Возьмем уже знакомый нам список студентов и создадим функцию, которая вычисляет средний балл:
students = [
("Анна", 72),
("Борис", 85),
("Виктор", 90),
("Даша", 64),
("Ирина", 100),
("Сергей", 77)
]
def average_grade(data):
"""Вычисляет средний балл студентов"""
total = sum([x[1] for x in data])
return total / len(data)
Теперь можно использовать эту функцию в другой, которая выводит результат в удобной форме:
def show_average(calc_function, dataset):
print("Средний балл:", calc_function(dataset))
show_average(average_grade, students)
Результат выполнения:
Средний балл: 81.33333333333333
Итак, мы передали функцию average_grade как аргумент в show_average. Первая отвечает за расчет, вторая — за вывод. Подобный прием делает код модульным: расчеты и оформление результатов можно менять независимо друг от друга.
Советы по использованию функций в Python
Функции помогают сделать код чище, понятнее и удобнее для сопровождения. Но чтобы они действительно были вам полезны, а не, наоборот, превращали код в хаос, стоит придерживаться нескольких правил:
- Делите задачи на части. Пусть каждая функция отвечает за что-то одно: вычисление среднего балла, сохранение данных, вывод результата. Если в функции сразу десятки разных действий, ее сложно читать и исправлять.
- Давайте осмысленные имена. Название должно сразу говорить, что делает функция: calculate_average(), print_report(), send_email(). Сокращения и абстрактные слова вроде do_task() только запутают.
- Используйте аргументы по умолчанию. Это избавляет от лишнего дублирования. Например, если функция почти всегда работает с одним и тем же значением, его удобно указать в параметре как стандартное.
- Не забывайте про документацию. Даже короткий docstring (описание в тройных кавычках) помогает быстро понять назначение функции без чтения кода.
- Следите за размером. Если функция разрослась до нескольких экранов, это сигнал, что ее стоит разбить на части.
- Проверяйте возвращаемые значения. Если функция что-то вычисляет, убедитесь, что она действительно возвращает результат через return, а не только печатает его.
- Используйте встроенные функции. Перед тем как писать собственный цикл для подсчета или сортировки, проверьте — возможно, за вас это уже делает sum(), max(), sorted() или другая встроенная функция.
- Не злоупотребляйте рекурсией. Она удобна, но при неправильном использовании легко приводит к ошибке переполнения стека.
- Тестируйте функции отдельно. Запускайте их на простых данных, чтобы убедиться, что они работают так, как задумано.
Заключение
Функции в Python дают возможность собрать разрозненные куски программы в понятную и гибкую систему. С их помощью можно управлять логикой, разносить вычисления по уровням сложности и быстро адаптировать код под новые задачи.
В Python достаточно много функций, которые помогают решать задачи разной сложности. Одни помогают упростить рутину, другие — оптимизировать работу с памятью или асинхронными процессами. Попробуйте разные подходы на практике и найдите тот уровень гибкости, который действительно облегчит вашу задачу, а не усложнит ее.