Функции в Питоне | Программирование на Питоне

Функции в Питоне

Все курсы > Программирование на Питоне > Занятие 6

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

функции в математике и программировании

Зачем это нужно? Если у вас есть код, который используется несколько раз в рамках программы, то функция позволяет написать код один раз, а потом просто вызывать его (call a function) при необходимости. Так мы не дублируем код и лучше его структурируем.

Откроем ноутбук к этому занятию

Встроенные функции

В Питоне есть множество встроенных в (1) стандартный функционал (built-in functions) и (2) дополнительные библиотеки (library functions) функций, и мы много раз их использовали.

Рассмотрим функцию для создания гистограммы plt.hist(). Вначале импортируем библиотеки.

Сгенерируем данные, которые передадим этой функции (эти же данные мы создавали и использовали на восьмом занятии вводного курса).

Теперь построим гистограмму, передав ей в качестве параметров и аргументов наши данные и количество интервалов.

параметры функции plt.hist()

Как мы видим, достаточно обратиться к соответствующей библиотеке ( plt), вызвать эту функцию по имени ( hist) и задать параметры и их аргументы ( heightи bins = 10), и будет исполнен тот код, который заложили в нее создатели библиотеки Matplotlib.

Теперь несколько слов про параметры и аргументы функции.

Параметры и аргументы функции

Для начала определимся с терминами:

  • параметр — это то, что запрашивает функция при вызове (например, bins, количество интервалов)
  • аргумент — значение этого параметра (в нашем случае, 10).

Возникает вопрос, что же такое height? Логично предположить, что это аргумент (ведь это наши данные). Но тогда как функция узнает, какому параметру он соответствует?

Все дело в том, что параметры и их аргументы могут быть позиционными (positional) и именованными (keyword).

позиционные и именованные аргументы функции

В первом случае, достаточно указать аргумент и поставить его в правильном порядке (позиции). Функция сама поймет, какой параметр ей передают. Во втором случае, нужно указать и название параметра, и аргумент.

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

использование именованных параметров функции plt.hist()

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

аргументы по умолчанию функции plt.hist()

Как вы видите, результат во всех трех случаях совершенно одинаковый.

Если вы сомневаетесь в том, какие параметры принимает функция и что является результатом ее работы, полезно обратиться к документации в Интернете. Например, по функции plt.hist() ее можно найти вот здесь⧉.

Стоит отметить, что функция может как принимать один, два или несколько параметров, так и не принимать их вовсе. Например, функция print(), если не указывать параметры, выдает пустую строку.

Функции и методы

Некоторые функции называются методами. Методы — это функции, которые можно применить только к конкретному объекту. Другими словами, если обычная функция будет выполнена «сама по себе», это просто участок кода, которому дали имя, то методу для исполнения нужен объект (например, строка, список или словарь). При этом, что важно, у каждого объекта свои методы.

Предположим, у нас есть строка, и мы хотим сделать первую букву каждого слова заглавной. Для этого у строки есть метод .title().

А теперь попробуем применить этот метод к списку, состоящему из тех же слов, что и строка.

ошибка при применении строкового метода .title() к списку

Как мы видим, Питон выдал ошибку.

Собственные функции в Питоне

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

Функции не обязательно должны быть встроены в базовый функционал или библиотеки. Мы вполне можем объявлять (т.е. создавать) собственные функции (user-defined functions). Рассмотрим пример.

Теперь давайте разберем каждый элемент этого кода. Вначале посмотрим как объявить функцию (declare a function).

синтаксис собственной функции
  • ключевое слово def необходимо для объявления функции;
  • далее идут название функции, которое вы сами определяете; и
  • параметры, которые может принимать ваша функция;
  • после двоеточия на новой строке с отступом идет так называемое тело функции, то есть то, что будет исполняться при вызове функции;
  • в конце ставится ключевое слово return, возвращающее результат работы функции.

Затем, когда это необходимо, мы можем вызвать функцию (call a function), указав ее имя и передав, при их наличии, соответствующие аргументы внутри круглых скобок.

вызов собственной функции

Пустое тело функции

Оставлять тело функции совсем пустым нельзя. Нужно как минимум указать ключевое слово return или оператор pass.

Хотя может показаться, что в этом случае функция ничего не выдает, на самом деле это не так. И в том, и в другом случае функция возвращает особый тип данных None (отсутствие значения).

Функция print() вместо return

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

Хотя визуально вывод идентичен, отличие все-таки есть:

  • Использование return возвращает значение функции (в нашем случае значение переменной res) и прерывает ее работу
  • Функция print() просто выводит это значение пользователю и не влияет на дальнейшее исполнение кода, если он есть

Параметры собственных функций

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

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

И конечно функция может изначально не иметь параметров.

Аннотация функции

Аннотация функции (function annotation) позволяет явно прописать тип данных параметров (parameter annotation) и возвращаемых значений (return annotation).

Аннотация не является обязательной и никак не вляет на выполнение кода.

Дополнительные возможности функций

Вызов функции можно совмещать с арифметическими операциями.

Доступны и логические операции.

Если результатом вывода является строка, то у этой строки также есть индекс.

Функция может не использовать параметры, но получать данные от пользователя через input().

Появится окно для ввода числа.

функция может получать данные от пользователя

Введем число пять и посмотрим на результат.

Результат вызова функции

Функция не обязательно возвращает только число или строку, это может быть, в частности, список, кортеж или словарь. Рассмотрим несколько примеров.

Функция может возвращать сразу несколько значений.

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

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

Функция может возвращать и логическое значение. Давайте объявим функцию, которая проверяет четное ли ей передали число (и в этом случае вернет True) или нечетное (и тогда False).

Использование библиотек

Внутри функций можно использовать дополнительные библиотеки Питона. Например, применим функцию mean() библиотеки Numpy для расчета среднего арифметического.

Глобальные и локальные переменные

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

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

Функция была вызвана без проблем. При этом если мы попробуем обратиться к переменной local_name вне этой функции, Питон выдаст ошибку.

ошибка при вызове локальной переменной вне своей функции

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

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

Функция всегда «предпочтет» содержащуюся в ней локальную переменную.

При этом значение глобальной переменной для остального кода не изменится.

Анонимные или lambda-функции

Функции создают не только через ключевое слово def и название функции. Можно использовать слово lambda и вообще обойтись без названия. Приведем простой пример.

Теперь давайте в деталях рассмотрим, как мы получили такой результат.

синтаксис lambda-функции
  • вначале ставится ключевое слово lambda
  • за ним идут передаваемые параметры
  • через двоеточие пишется исполняемое выражение

Разумеется, ничто не мешает поместить этот же функционал в обычную функцию.

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

Lambda-функция внутри функции filter()

Предположим, у нас есть список чисел, и мы хотим оставить в нем только те числа, которые больше 10. Давайте решим эту задачу с помощью функции filter() и lambda-функции.

Функция filter() принимает два параметра:

  • Во-первых, еще одну функцию, выполняющую роль критерия; она выдает True, если элемент нужно оставить, и False — если убрать
  • Во-вторых, набор элементов, которые нужно отфильтровать в виде списка, кортежа или множества

Получив их, filter() применяет критерий (функцию) к каждому из элементов набора.

Посмотрим на реализацию на Питоне. Вначале создадим список.

Теперь зададим фильтрующую lambda-функцию.

После этого поместим criterion и nums в функцию filter(). Так как сама функция filter() вернет нам не список, а специальный объект iterator, его в свою очередь нужно преобразовать обратно в список с помощью функции list().

Чаще такой функционал записывают в одну строчку.

И в этом и заключается удобство lambda-функции, ее не надо объявлять заранее. Через обычную функцию код выглядел бы так.

Lambda-функция внутри функции sorted()

Теперь рассмотрим lambda-функцию в коде, который мы использовали на занятии по рекомендательным системам. Воспроизведем в миниатюре часть решения из того занятия.

В какой-то момент мы получили список из кортежей. И в каждом кортеже был индекс фильма и расстояние до него.

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

Мы взяли функцию sorted() и в качестве параметра ключа, по которому сортировать список (key), передали ей lambda-функцию, принимающей кортеж из двух элементов на входе, и выдающей второй его элемент на выходе.

Параметр reverse = True, напомню, задает сортировку по убыванию.

Немедленно вызываемые функции

Lambda-функции относятся к так называемым немедленно вызываемым функциям или immediately invoked function expressions (IIFE). Это означает, что мы можем одновременно объявить и вызвать такую функцию.

*args и **kwargs

Прежде чем завершить, поговорим про еще одну важную тему, а именно про так называемые *args (сокращение от arguments) и **kwargs (keyword arguments).

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

Рассмотрим на примере. Начнем с *args.

*args

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

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

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

ошибка при использовании *args

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

Как вы видите, главным элементом здесь является оператор распаковки * (unpacking operator). Он принимает все передаваемые в функцию числа и формирует из них кортеж.

Затем мы проходимся по элементам этого кортежа, рассчитываем их сумму и делим на количество элементов. Использовать слово args не обязательно, мы назвали наш позиционный аргумент nums.

Если мы по какой-то причине захотим передать функции список, мы можем это сделать.

В этом случае мы передаем название списка со звездочкой *.

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

**kwargs

При использовании **kwargs происходит почти то же самое за тем исключением, что мы распаковываем именованные, а не позиционные аргументы. И распаковываем их в словарь, а не в список. Сразу посмотрим на примере.

Приведем более сложный пример. Напишем функцию, которая на вход примет произвольное количество чисел (позиционный аргумент), преобразует в кортеж (*args) и рассчитает среднее арифметическое (mean) и среднее квадратическое отклонение (standard deviation).

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

Вызовем функцию simple_stats() и передадим ей числа и именованные аргументы.

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

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

Количество именованных аргументов в **kwargs может быть любым. Ничто не мешает нам добавить еще один параметр.

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

В заключение скажу, что все приведенные выше примеры являются учебными и без *args и **kwargs здесь конечно можно обойтись. На практике, они применяются в более сложных конструкциях, например, в так называемых декораторах, однако эта тема выходит за рамки сегодняшнего занятия.

Подведем итог

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

  • использовать готовые функции, которые уже встроены либо в базовый функционал, либо в дополнительную библиотеку;
  • объявлять собственные функции через ключевое слово def и название функции; а также
  • создавать анонимные или lambda-функции, которые очень удобно применять там, где в полноценных собственных функциях нет необходимости

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

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

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

Вопросы для закрепления

Какие три вида функций мы изучили?

Посмотреть правильный ответ

Какие бывают параметры и аргументы функции?

Посмотреть правильный ответ

Какова область видимости локальной переменной?

Посмотреть правильный ответ

В ноутбуке к лекции приведены дополнительные упражнения⧉.

В следующий раз мы подробнее рассмотрим списки, кортежи и множества.


Ответы на вопросы

Вопрос. Скажите, а почему используется символ нижнего подчеркивания в названии переменной list_?

Ответ. Не стоит использовать просто list для именования переменных, потому что это слово зарезервировано для названия функции. Аналогично не стоит использовать dict, tuple, set и т.д. При этом некоторые слова использовать в качестве названия переменных просто не получится. Питон выдаст ошибку. Это, например, if, for, True, False, import и т.п.

Символ подчеркивания является одним из вариантов решения.