Функции в Python
easy

Функции в Python

И почему мы постоянно ими пользуемся

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

Это статья про теоретические вещи, хотя и с примерами. Если хотите собрать что-нибудь интересное, вот пара наших проектов:

Что такое функция

Функции в Python (да и в программировании в целом) нужны для того, чтобы делать код проще и удобнее.

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

Функции — это готовые элементы программы, которым можно присвоить имена и научить выполнять нужные алгоритмы. Можно сказать функции: я буду приносить тебе вот эти данные, а тебе с ними нужно будет сделать то-то и то-то.

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

Функциональная парадигма программирования

Одна из основных характеристик программ — парадигма программирования. Это то, как разработчик пишет код, какие подходы и технологии языка использует, а какие — нет.

Многие думают, что функциональное программирование — это когда используешь функции, но это не так. 

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

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

Синтаксис функций

Для создания функций в Python есть ключевой параметр-слово def. В Python def означает, что сейчас будет объявлена функция.

Порядок определения функции такой:

  • пишем def, чтобы Python понял, что дальше идёт функция;
  • даём функции имя;
  • после имени в скобках перечисляем аргументы, с которыми будет работать функция;
  • описываем алгоритм работы с аргументами.

Вот пример функции describe_pet, которая выводит описание домашнего питомца. Эта функция принимает два аргумента: тип животного и его имя. Если вызвать её и передать эти два аргумента, она вставит их в строку и выведет на экран:

# объявляем функцию и даём ей имя
def describe_pet(animal_type, pet_name):
   """
   Выводит информацию о питомце.

   :param animal_type: Тип животного (например, "кошка", "собака").
   :param pet_name: Имя питомца.
   """
   print(f"У вас есть {animal_type}, ее зовут {pet_name}.")


# вызываем функцию и передаём ей аргументы для работы
describe_pet("кошка", "Мурка")

Результат вызова:

У вас есть кошка, ее зовут Мурка.

Возвращаемые значения (return)

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

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

# объявляем функцию
def add(a, b):
   # говорим функции возвращать результат сложения
   return a + b


# сохраняем результат в переменную
result = add(3, 5)
# выводим результат на экран
print(result)

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

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

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

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

Пример — возвращаем минимальное и максимальное значение из списка:

# объявляем функцию
def min_max(numbers):
   # просим вернуть минимальное и максимальное значение
   # из коллекции элементов, которую мы передадим
   # эти значения вернутся в виде кортежа
   return min(numbers), max(numbers)


# распаковываем значения в отдельные переменные minimum и maximum
minimum, maximum = min_max([10, 20, 30])
# выводим значения на экран
print(f"Минимум: {minimum}, Максимум: {maximum}")

При вызове этого блока кода на экране получим:

Минимум: 10, Максимум: 30

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

Для создания пустой функции её нужно объявить и в теле функции прописать только одно слово — pass.

# объявляем пустую функцию
def placeholder_function():
   pass

Функции и процедуры

Процедуры в Python — это функции, которые ничего не возвращают. 

Наша функция describe_pet() про вывод информации о домашнем животном — это процедура, потому что в ответ мы ничего не получаем. А add() с возвращением суммы двух чисел — классическая функция. 

👉 Правило простое: чтобы превратить функцию в процедуру в Python, нужно не использовать команду return() в конце функции.

Объявление и вызов функций

Функцию можно объявить в любом месте кода, но вызвать — только после объявления.

Так происходит потому, что Python — интерпретируемый язык, и весь код перед исполнением будет переработан в список инструкций для машины. Этот список интерпретатор Python будет выполнять построчно. Когда интерпретатор встречает вызов функции, к этому моменту он уже должен знать, что это за функция и как она определена. Если функция ещё не объявлена — Python выдаст ошибку и остановит выполнение кода.

Область видимости функций

Переменные в Python-программе имеют три области видимости. Область видимости отвечает за то, в каком месте кода можно использовать переменную, которая объявлена где-то в другом месте этой же программы.

Областей видимости при использовании функций в Python три. 

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

Пример — локальная переменная x. Её нельзя будет использовать в других местах программы, только внутри этой функции (то есть там, где она объявлена):

# объявляем функцию с локальной переменной
def local_example():
   # эта переменная видна только внутри функции
   x = 10

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

Этот код:

# объявляем объемлющую функцию
def outer_function():
   # переменная в области объемлющей функции
   y = 20

   # объявляем вложенную функцию
   def inner_function():
       # переменная в области объемлющей функции доступна
       # для использования во вложенной функции, но не для изменения
       print(f"Переменная из объемлющей области y = {y}")
   
   # вызываем вложенную функцию
   inner_function()

# вызываем объемлющую функцию
outer_function()

Выведет в консоли такой результат:

Переменная из объемлющей области y = 20

Если нужно сделать так, чтобы вложенная функция могла изменять значения в объемлющей, нужно отметить эти значения словом nonlocal:

# объявляем объемлющую функцию
def outer_function():
   # переменная в области объемлющей функции
   y = 20

   # объявляем вложенную функцию
   def inner_function():
       # указываем, что хотим изменить переменную объемлющей области
       nonlocal y
       # изменяем переменную
       y = 30
       print(f"Вложенная функция изменила y на {y}")

   # вызываем вложенную функцию
   inner_function()
   print(f"После изменения: y = {y}")


# вызываем объемлющую функцию
outer_function()

Глобальная область видимости охватывает весь код. Переменные, объявленные вне функций, доступны везде, если их не перекрывают локальные переменные с тем же названием (в Python так можно). 

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

# глобальная переменная в теле кода
z = 50 


# объявляем функцию
def global_example():
   # используем доступ к глобальной переменной
   print(f"Глобальная переменная z = {z}")


# вызываем функцию
global_example()

Результат в консоли:

Глобальная переменная z = 50

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

# глобальная переменная
z = 50
# проверяем переменную
print(f"До вызова функции z = {z}")


# объявляем функцию
def modify_global():
   # указываем, что хотим изменить глобальную переменную
   global z
   # изменяем глобальную переменную
   z = 100
   # выводим результат изменения
   print(f"Внутри функции z = {z}")


# вызываем функцию
modify_global()
# проверяем результат работы функции
print(f"После вызова функции z = {z}")

Запускаем:

До вызова функции z = 50

Внутри функции z = 100

После вызова функции z = 100

Аргументы функций

Всё, что мы передаём в функцию, называется аргументами. Вот что нужно знать про них.

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

# объявляем функцию и передаём позиционные аргументы
def greet(name, age):
   # пишем тело функции
   print(f"Привет, {name}! Тебе {age} лет.")


# аргументы передадутся в таком порядке:
# name="Алиса", age=30
greet("Алиса", 30) 

Если при передаче значений поменять их местами, то в name вместо "Алиса" функция запишет 30, а вместо возраста — "Алиса".

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

# объявляем функцию и передаём позиционные аргументы
def greet(name, age):
   # пишем тело функции
   print(f"Привет, {name}! Тебе {age} лет.")


# при такой передаче аргументов их порядок можно менять
greet(age=30, name="Алиса")

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

# объявляем функцию и передаём для аргумента
# age значение по умолчанию
def greet(name, age=18):
   # пишем тело функции
   print(f"Привет, {name}! Тебе {age} лет.")


# вызываем функцию без передачи age
greet("Алиса")
# вызываем функцию и изменяем значение age
greet("Боб", 25)

Аргументы переменной длины (*args и **kwargs) нужны, если мы не знаем, сколько аргументов нам понадобится. Чем они отличаются:

  • *args используется для передачи произвольного количества позиционных аргументов.
  • **kwargs используется для передачи произвольного количества именованных аргументов.

Вот как это будет выглядеть для позиционных аргументов:

# объявляем функцию с произвольным количеством позиционных аргументов
def sum_numbers(*args):
   # пишем тело функции
   total = sum(args)
   # выводим на экран сумму позиционных аргументов
   print(f"Сумма: {total}")


# вызываем функцию
sum_numbers(1, 2, 3, 4)

Такой код выведет в консоли:

Сумма: 10

Функция не проверяет аргументы автоматически. Если в список чисел попробовать передать строку, мы получим ошибку. Поэтому в реальном коде нужно предусмотреть вариант неправильного ввода и обработать его.

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

# объявляем функцию с произвольным количеством именованных аргументов
def describe_person(**kwargs):
   # пишем тело функции
   for key, value in kwargs.items():
       # выводим на экран словарь вида аргумент:значение
       print(f"{key}: {value}")


# вызываем функцию
describe_person(name="Алиса", age=30, city="Москва")

Получаем:

name: Алиса

age: 30

city: Москва

Передача по значению и по ссылке означает два вида передачи аргументов в функцию и основным принципам работы Python.

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

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

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

Неизменяемые объекты не могут быть изменены внутри функции. Поэтому это может выглядеть как передача по значению, но на самом деле в Python всё передаётся по ссылке.

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

# объявляем функцию с позиционными аргументами
def describe_person(name, age, city):
   # пишем тело функции
   print(f"{name}, {age} лет, живёт в {city}")


# создаём словарь
person = {"name": "Алиса", "age": 30, "city": "Москва"}
# распаковываем словарь функцией
describe_person(**person)

При запуске Python распакует переменную со словарём и разложит значения по указанным в логике местам:

Алиса, 30 лет, живёт в Москва

Lambda-функции (анонимные)

Def в Python даёт начало обычным, классическим функциям, но есть и другие.

Lambda-функция — короткая анонимная функция, которая создаётся с помощью ключевого слова lambda. Чаще всего они используются для создания простых функций, которые нужны в одном месте.

Как создать лямбда-функцию:

  • написать ключевое слово lambda;
  • указать аргументы;
  • поставить двоеточие;
  • записать логику функции.

Пример, как может определяться простая lambda-функция подсчёта квадрата числа:

# объявляем переменную и присваиваем ей лямбда-функцию
square = lambda x: x ** 2
# выводим результат работы
print(square(5))

В консоли получаем ответ:

5

Напоследок: чистые функции и побочные эффекты

Чистая функция — функция, которая:

  • Возвращает результат, зависящий только от её входных данных. Это ведёт к воспроизводимости — одинаковый ввод всегда приводит к одинаковому результату.
  • Не изменяет внешние состояния — не изменяет глобальные переменные, не влияет на файлы или базы данных.

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

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

Обложка:

Алексей Сухов

Корректор:

Елена Грицун

Вёрстка:

Кирилл Климентьев

Соцсети:

Юлия Зубарева

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Вам может быть интересно
easy