Функции в Python: что такое def и как их объявлять

Функции в Python: что такое def и как их объявлять


Коротко. Функция в Python — это именованный блок кода, который можно вызывать сколько угодно раз. Объявляют функцию ключевым словом def, после него идет имя, круглые скобки с параметрами и двоеточие, а тело пишется с отступом. Результат работы возвращают через return. Если return не написан, функция вернет None.


Зачем нужны функции


Представьте программу, которая считает итоговую оценку студента в трех местах: при выводе на экран, при записи в файл и при отправке отчета. Без функций формулу придется скопировать три раза. Меняется правило расчета — надо не забыть исправить все три копии, и одну обязательно забудете.


Функция решает эту проблему: код пишется один раз, а дальше вызывается по имени. Отсюда три практических выгоды.


Нет дублирования. Правку вносите в одном месте, и она работает везде.


Код читается. Строка total = calculate_score(math, russian, informatics) понятна без пояснений, а десять строк вычислений на том же месте — нет.


Задача разбивается на части. Большую программу проще писать и отлаживать, когда она собрана из небольших понятных кусков.


Простое правило для новичка: как только вы копируете фрагмент кода второй раз — пора делать функцию.


Что такое функция и как ее объявить


Функция объявляется ключевым словом def — сокращение от английского define, «определить». Общая схема выглядит так:


def имя_функции(параметры):


"""Строка документации."""


тело функции


return результат


Разберем на примере. Функция принимает два числа и возвращает их сумму:


def add(a, b):


return a + b


Здесь def сообщает интерпретатору, что дальше идет объявление функции, add — ее имя, a и b — параметры, а строка с отступом — тело.


Обратите внимание на четыре обязательных элемента синтаксиса: ключевое слово, круглые скобки (даже если параметров нет), двоеточие в конце строки и отступ у тела. Пропуск любого из них дает ошибку.


Важная деталь: объявление функции ее не выполняет. Python просто запоминает, что такая функция существует. Код внутри запускается только при вызове.


Как вызвать функцию


Чтобы вызвать функцию, напишите ее имя и передайте аргументы в скобках.


def add(a, b):


return a + b


result = add(3, 5)


print(result) # 8


Функцию можно вызывать сколько угодно раз с разными данными — в этом и есть смысл.


print(add(10, 20)) # 30


print(add(1.5, 2.5)) # 4.0


Одно ограничение: вызвать функцию можно только после ее объявления. Python читает файл сверху вниз, и если вызов встретится раньше определения, программа упадет с ошибкой NameError.


Параметры и аргументы: в чем разница


Эти два слова часто путают, хотя разница простая.


Параметр — это имя переменной в объявлении функции. В примере выше параметры — a и b.


Аргумент — это конкретное значение, которое передают при вызове. В строке add(3, 5) аргументы — тройка и пятерка.


Короткая формула: параметры описывают, что функция ожидает получить, аргументы — что она получила на самом деле.


Позиционные и именованные аргументы


Аргументы можно передавать двумя способами.


Позиционные распределяются по порядку: первый аргумент попадает в первый параметр, второй во второй.


def greet(name, city):


print(f"Привет, {name} из города {city}")


greet("Азиз", "Ташкент")


# Привет, Азиз из города Ташкент


Порядок здесь критичен. Поменяйте аргументы местами — и получите бессмыслицу вроде «Привет, Ташкент из города Азиз».


Именованные передаются с указанием имени параметра. Тогда порядок не важен, а вызов становится понятнее.


greet(city="Самарканд", name="Дилноза")


# Привет, Дилноза из города Самарканд


Именованные аргументы особенно полезны, когда параметров много или когда среди них есть флаги вида True и False. Сравните: send_report(data, True, False) — непонятно, что означают эти булевы значения. А send_report(data, notify=True, archive=False) читается сразу.


Смешивать два способа можно, но с ограничением: позиционные аргументы всегда идут первыми.


greet("Азиз", city="Ташкент") # работает


greet(name="Азиз", "Ташкент") # SyntaxError


Значения по умолчанию


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


def greet(name, greeting="Привет"):


print(f"{greeting}, {name}!")


greet("Азиз") # Привет, Азиз!


greet("Азиз", "Добрый вечер") # Добрый вечер, Азиз!


Такие параметры делают функцию гибче: типовой случай работает без лишних аргументов, а нетиповой можно настроить.


Единственное правило: параметры со значениями по умолчанию идут после обычных. Иначе Python не поймет, куда распределять позиционные аргументы, и выдаст синтаксическую ошибку.


Ловушка изменяемого значения по умолчанию


А вот подвох, о котором почти не пишут в статьях для новичков, но на котором спотыкаются все.


Значение по умолчанию вычисляется один раз — в момент объявления функции, а не при каждом вызове. Для чисел и строк это незаметно, а для списков и словарей приводит к странному поведению:


def add_task(task, tasks=[]):


tasks.append(task)


return tasks


print(add_task("купить хлеб")) # ['купить хлеб']


print(add_task("позвонить маме")) # ['купить хлеб', 'позвонить маме']


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


Правильное решение — передавать None и создавать объект внутри:


def add_task(task, tasks=None):


if tasks is None:


tasks = []


tasks.append(task)


return tasks


Запомните правило: в значениях по умолчанию используйте только неизменяемые объекты — числа, строки, кортежи, None.


Произвольное число аргументов: args и kwargs


Иногда заранее неизвестно, сколько значений придет в функцию. Для этого есть две специальные конструкции.


Звездочка перед именем собирает все лишние позиционные аргументы в кортеж. По традиции параметр называют args, но имя может быть любым — работает именно звездочка.


def total(*numbers):


return sum(numbers)


print(total(1, 2, 3)) # 6


print(total(10, 20, 30, 40)) # 100


Две звездочки собирают именованные аргументы в словарь. Традиционное имя — kwargs.


def show_profile(**data):


for key, value in data.items():


print(f"{key}: {value}")


show_profile(name="Дилноза", age=21, city="Ташкент")


# name: Дилноза


# age: 21


# city: Ташкент


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


Распаковка при вызове


Звездочки работают и в обратную сторону. Если данные уже лежат в списке или словаре, их можно развернуть прямо в аргументы:


def greet(name, city):


print(f"Привет, {name} из города {city}")


values = ["Азиз", "Ташкент"]


greet(*values)


person = {"name": "Дилноза", "city": "Самарканд"}


greet(**person)


Одна звездочка раскладывает список по позиционным параметрам, две — словарь по именованным. Ключи словаря при этом должны точно совпадать с именами параметров.


Порядок параметров и специальные разделители


В объявлении функции параметры идут в строгом порядке: сначала обычные, потом со значениями по умолчанию, затем *args, и в конце **kwargs.


def report(title, author, draft=False, *sections, **options):


...


Есть еще два разделителя, которые встречаются в чужом коде и в документации.


Звездочка отдельным параметром делает все, что идет после нее, только именованным. Это защита от путаницы в вызове:


def create_user(name, *, is_admin=False):


...


create_user("Азиз", is_admin=True) # работает


create_user("Азиз", True) # TypeError


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


Оператор return: возвращаем результат


return завершает работу функции и отдает значение наружу.


def square(x):


return x ** 2


result = square(4)


print(result) # 16


Ключевое отличие от print часто путают новички. print просто печатает текст на экран, и использовать этот текст дальше невозможно. return передает значение в программу, где его можно сохранить, посчитать, записать в файл.


def bad(x):


print(x ** 2) # напечатает, но вернет None


def good(x):


return x ** 2 # вернет значение


total = good(4) + good(5) # 41


Функция может вернуть несколько значений — они соберутся в кортеж, который удобно распаковать:


def min_max(numbers):


return min(numbers), max(numbers)


low, high = min_max([10, 5, 30])


print(low, high) # 5 30


Return прерывает функцию. Все, что написано после него, не выполнится. Это используют для ранних выходов, которые упрощают код:


def divide(a, b):


if b == 0:


return None


return a / b


Такая проверка в начале функции называется охранным условием: неподходящий случай отсекается сразу, и основная логика остается без вложенных if.


Функция без return возвращает None. Это не ошибка, а нормальное поведение — так работают функции, которые только выполняют действие, например печатают отчет.


Область видимости переменных


Переменная, созданная внутри функции, снаружи не существует. Это называется локальной областью видимости.


def calculate():


result = 42


return result


calculate()


print(result) # NameError: name 'result' is not defined


Такое поведение — не ограничение, а защита. Функции не мешают друг другу, и переменная i в одной из них не сломает переменную i в другой.


Переменные, объявленные вне функций, находятся в глобальной области. Читать их изнутри функции можно:


tax = 0.12


def net_salary(gross):


return gross - gross * tax


А вот изменить глобальную переменную изнутри просто так нельзя: Python решит, что вы создаете новую локальную с тем же именем. Для изменения нужно ключевое слово global:


counter = 0


def increase():


global counter


counter += 1


Есть и третья область — объемлющая. Она появляется, когда одна функция объявлена внутри другой. Вложенная функция видит переменные внешней, а чтобы их менять, нужно слово nonlocal:


def make_counter():


count = 0


def counter():


nonlocal count


count += 1


return count


return counter



call = make_counter()


print(call()) # 1


print(call()) # 2


Практический совет: global и nonlocal — инструменты на крайний случай. Функция, которая тихо меняет внешние переменные, ломает предсказуемость программы и усложняет отладку. Лучше передавать данные через параметры и возвращать результат через return.


Как функция обращается с аргументами


Частый вопрос новичка: может ли функция испортить переданные ей данные? Ответ зависит от типа.


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


def increase(number):


number += 1


return number


value = 5


increase(value)


print(value) # 5 — не изменилось


А вот изменяемые объекты функция меняет по-настоящему:


def add_item(items):


items.append("новый")


basket = ["хлеб"]


add_item(basket)


print(basket) # ['хлеб', 'новый'] — изменился


Иногда это именно то, что нужно. Но если менять исходные данные нежелательно, работайте внутри с копией:


def add_item(items):


result = items.copy()


result.append("новый")


return result


Docstring: документируем функцию


Первая строка в теле функции может быть текстовым описанием в тройных кавычках — это докстринг. Он попадает в справку и виден в подсказках редактора.


def net_salary(gross, tax=0.12):


"""


Считает зарплату на руки.


gross: сумма до вычета налога


tax: ставка налога, по умолчанию 0.12


Возвращает: сумму после вычета


"""


return gross - gross * tax



print(net_salary.__doc__)


Для маленьких очевидных функций докстринг не обязателен. Но как только логика становится неочевидной, короткое описание экономит время и вам, и коллегам. Проверенное правило: если объяснение занимает больше двух предложений — возможно, функция делает слишком много и ее стоит разбить.


Аннотации типов


С версии 3.5 в объявлении функции можно указывать ожидаемые типы. Python их не проверяет и на выполнение они не влияют, но код становится понятнее, а редактор начинает подсвечивать ошибки заранее.


def net_salary(gross: float, tax: float = 0.12) -> float:


return gross - gross * tax


Запись gross: float означает «ожидается дробное число», а стрелка -> float описывает тип результата. В командных проектах аннотации давно стали нормой, поэтому привыкать к ним стоит с первых учебных программ. Подробнее про сами типы есть отдельная статья блога.


Lambda: функция в одну строку


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


square = lambda x: x ** 2


print(square(5)) # 25


Так писать не принято: если функции нужно имя, объявляйте ее через def. Настоящее применение лямбды — короткая одноразовая функция, которую передают в другую функцию:


students = [("Азиз", 85), ("Дилноза", 92), ("Тимур", 78)]


students.sort(key=lambda item: item[1], reverse=True)


print(students)


# [('Дилноза', 92), ('Азиз', 85), ('Тимур', 78)]


Здесь лямбда объясняет, по какому полю сортировать. Выносить такую мелочь в отдельную именованную функцию было бы избыточно.


Функция как объект


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


def shout(text):


return text.upper()


action = shout


print(action("привет")) # ПРИВЕТ


Обратите внимание: shout без скобок — это сама функция, а shout("привет") — ее вызов. Забытые скобки при вызове дают одну из самых загадочных для новичка ошибок: программа не падает, но вместо результата в переменной оказывается что-то вроде <function shout at 0x7f...>.


Именно на этом свойстве построены встроенные функции map, filter и sorted, которые принимают функцию параметром. Разбор одной из них есть в статье про map.


Рекурсия: функция вызывает саму себя


Функция может вызвать саму себя — такой прием называется рекурсией. Классический пример — факториал:


def factorial(n):


if n <= 1:


return 1


return n * factorial(n - 1)


print(factorial(5)) # 120


Обязательное условие — базовый случай, при котором рекурсия останавливается. Здесь это n <= 1. Без него функция будет вызывать себя бесконечно, пока Python не прервет ее ошибкой о превышении глубины рекурсии.


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


Как называть функции


В Python принят стандарт оформления кода PEP 8, и по нему имена функций пишут строчными буквами, а слова разделяют подчеркиванием: calculate_total, send_email, get_user_name.


Хорошее имя функции — это глагол или глагольная фраза, потому что функция что-то делает. Имя должно объяснять, что происходит, без чтения тела: check_password понятнее, чем check, а f1 не говорит вообще ни о чем.


Отдельно избегайте имен встроенных функций. Назвав свою функцию sum, list или type, вы перекроете стандартную и потом долго будете искать, почему код ведет себя странно.


Типичные ошибки новичков


Вызов до объявления. Python читает файл сверху вниз, поэтому определение должно идти раньше вызова.


Забытые скобки при вызове. my_function возвращает саму функцию, а не результат ее работы. Нужны скобки: my_function().


Print вместо return. Функция печатает результат, но наружу не отдает ничего. Использовать значение дальше в программе не получится.


Изменяемое значение по умолчанию. Список или словарь в качестве значения по умолчанию сохраняет данные между вызовами.


Слишком большая функция. Если функция занимает сотню строк и делает пять разных дел, ее невозможно ни понять, ни протестировать. Одна функция — одна задача.


Злоупотребление global. Функция, которая меняет глобальные переменные, превращает отладку в квест. Передавайте данные аргументами.


Несовпадение числа аргументов. Ошибка TypeError: missing 1 required positional argument означает, что вы объявили параметров больше, чем передали при вызове.


Когда выносить код в функцию


  • Фрагмент кода повторяется хотя бы дважды
  • Кусок логики можно назвать одним понятным глаголом
  • Тело цикла или ветки if разрослось и мешает читать общую картину
  • Часть кода хочется протестировать отдельно
  • Логику планируется переиспользовать в другом проекте


И обратный сигнал: если функцию невозможно назвать коротко и понятно, скорее всего, она делает слишком много и ее стоит разделить.


Коротко о главном


Функция в Python объявляется через def, принимает параметры в круглых скобках и возвращает результат через return. Аргументы бывают позиционными и именованными, могут иметь значения по умолчанию, а конструкции со звездочками позволяют принимать произвольное их количество.


Переменные внутри функции живут в локальной области видимости и снаружи недоступны. Изменяемые объекты передаются по ссылке, поэтому функция может их изменить — это стоит учитывать. И два правила, которые уберегут от самых частых ошибок: не используйте изменяемые объекты как значения по умолчанию и не путайте return с print.


Функции — это переход от скрипта, который просто выполняется сверху вниз, к программе со структурой. Освоить Python с нуля до уровня реальных проектов можно на курсе Python в PROWEB — 8 месяцев, с 14 лет, с практикой и поддержкой в трудоустройстве. Если вы еще выбираете направление в IT, пройдите бесплатный IT-тест — он подскажет, что вам ближе.


Часто задаваемые вопросы


Что означает def в Python?


Это ключевое слово для объявления функции, сокращение от английского define — «определить». После def пишут имя функции, круглые скобки с параметрами и двоеточие, а тело функции оформляют отступом.


Чем параметр отличается от аргумента?


Параметр — это имя переменной в объявлении функции, оно описывает, что функция ожидает получить. Аргумент — конкретное значение, переданное при вызове. В def add(a, b) параметры это a и b, а в add(3, 5) аргументы это 3 и 5.


Чем return отличается от print?


print выводит текст на экран, и использовать его дальше в программе невозможно. return передает значение наружу, его можно сохранить в переменную, посчитать или записать в файл. Если функция должна отдавать результат, нужен именно return.


Что вернет функция без return?


Значение None. Это нормальное поведение для функций, которые только выполняют действие: печатают сообщение, записывают файл, отправляют письмо.


Зачем нужны args и kwargs?


Они позволяют принимать заранее неизвестное количество аргументов. Одна звездочка собирает лишние позиционные аргументы в кортеж, две звездочки собирают именованные аргументы в словарь. Имена args и kwargs традиционные, работают именно звездочки.


Почему нельзя ставить список значением по умолчанию?


Значение по умолчанию вычисляется один раз при объявлении функции, а не при каждом вызове. Список будет общим для всех вызовов и станет накапливать данные. Правильно передавать None и создавать список внутри функции.


Как изменить глобальную переменную внутри функции?


Через ключевое слово global перед именем переменной. Но злоупотреблять этим не стоит: функция, которая меняет внешние данные, усложняет отладку. Лучше передать значение параметром и вернуть результат через return.


Может ли функция изменить переданный ей список?


Да. Изменяемые объекты — списки, словари, множества — передаются по ссылке, и любые изменения внутри функции отражаются на оригинале. Если этого нужно избежать, работайте внутри с копией через метод copy().


Когда использовать lambda вместо def?


Когда нужна очень короткая функция из одного выражения, которую передают аргументом в другую функцию, например в sort или filter. Если функции нужно имя или тело длиннее одной строки, объявляйте ее через def.


Как правильно называть функции в Python?


По стандарту PEP 8 — строчными буквами с подчеркиванием между словами: calculate_total, get_user_name. Имя должно быть глаголом и объяснять, что функция делает. Имена встроенных функций вроде sum, list или type использовать нельзя.



Другие статьи