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

Зачем это нужно? Если у вас есть код, который используется несколько раз в рамках программы, то функция позволяет написать код один раз, а потом просто вызывать его при необходимости. Так мы не дублируем код и лучше его структурируем.
Откроем ноутбук к этому занятию⧉
Встроенные функции
В Питоне есть множество встроенных в (1) стандартный функционал (built-in functions) и (2) дополнительные библиотеки (library functions) функций, и мы много раз их использовали.
Рассмотрим функцию для создания гистограммы plt.hist(). Вначале импортируем библиотеки.
1 2 |
import matplotlib.pyplot as plt import numpy as np |
Сгенерируем данные, которые передадим этой функции (эти же данные мы создавали и использовали на восьмом занятии вводного курса).
1 2 3 4 5 |
# установим точку отсчета для воспроизведения такого же результата np.random.seed(42) # и сгенерируем данные о росте height = list(np.round(np.random.normal(180, 10, 1000))) |
Теперь построим гистограмму передав ей в качестве параметров и аргументов наши данные и количество интервалов.
1 2 |
plt.hist(height, bins = 10) plt.show() |

Как мы видим, достаточно обратиться к соответствующей библиотеке ( plt), вызвать эту функцию по имени ( hist) и задать параметры и их аргументы ( heightи bins = 10), и будет исполнен тот код, который заложили в нее создатели библиотеки Matplotlib.
Теперь несколько слов про параметры и аргументы функции.
Параметры и аргументы функции
Для начала определимся с терминами:
- параметр — это то, что запрашивает функция при вызове (например, bins, количество интервалов)
- аргумент — значение этого параметра (в нашем случае, 10).
Возникает вопрос, что же такое height? Логично предположить, что это аргумент (ведь это наши данные). Но тогда как функция узнает, какому параметру он соответствует?
Все дело в том, что параметры и их аргументы могут быть позиционными (positional) и именованными (keyword).

В первом случае, достаточно указать аргумент и поставить его в правильном порядке (позиции). Функция сама поймет, какой параметр ей передают. Во втором случае, нужно указать и название параметра, и аргумент.
Позиционный параметр можно сделать именованным, и тогда порядок важен не будет.
1 2 3 |
# данные в этой функции обозначаются через x plt.hist(bins = 10, x = height) plt.show() |

Кроме того, функция может иметь параметры с аргументами по умолчанию. Это делает такой параметр не обязательным, а значит упрощает и ускоряет вызов функции.
1 2 3 4 |
# у параметра bins есть аргумент по умолчанию (как раз 10 интервалов) # а значит, этот параметр можно не указывать plt.hist(height) plt.show() |

Как вы видите, результат во всех трех случаях совершенно одинаковый.
Если вы сомневаетесь в том, какие параметры принимает функция и что является результатом ее работы, полезно обратиться к документации в Интернете. Например, по функции plt.hist() ее можно найти вот здесь⧉.
Стоит отметить, что функция может как принимать один, два или несколько параметров, так и не принимать их вовсе. Например, функция print(), если не указывать параметры, выдает пустую строку.
1 2 3 |
print('Первая строка') print() print('Третья строка') |
1 2 3 |
Первая строка Третья строка |
Функции и методы
Некоторые функции называются методами. Методы — это функции, которые можно применить только к конкретному объекту. Другими словами, если обычная функция будет выполнена «сама по себе», это просто участок кода, которому дали имя, то методу для исполнения нужен объект (например, строка, список или словарь). При этом, что важно, у каждого объекта свои методы.
Предположим, у нас есть строка, и мы хотим сделать первую букву каждого слова заглавной. Для этого у строки есть метод .title().
1 2 3 4 5 |
# создаем строковый объект и some_string = 'machine learning' # применяем к нему метод .title() some_string.title() |
1 |
'Machine Learning' |
А теперь попробуем применить этот метод к списку, состоящему из тех же слов, что и строка.
1 2 |
some_list = ['machine', 'learning'] some_list.title() |

Как мы видим, Питон выдал ошибку.
Собственные функции в Питоне
Объявление и вызов функции
Функции не обязательно должны быть встроены в базовый функционал или библиотеки. Мы вполне можем объявлять (т.е. создавать) собственные функции (user-defined functions). Рассмотрим пример.
1 2 3 4 5 |
# создадим функцию, которая удваивает # любое передаваемое ей значение def double(x): res = x * 2 return res |
1 2 |
# и вызовем ее, передав число 2 double(2) |
1 |
4 |
Теперь давайте разберем каждый элемент этого кода. Вначале посмотрим как объявить функцию (declare a function).

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

Пустое тело функции
Оставлять тело функции совсем пустым нельзя. Нужно как минимум указать ключевое слово return или оператор pass.
1 2 3 4 5 |
# тело функции не может быть пустым def only_return(): # нужно либо указать ключевое слово return return |
1 |
only_return() |
1 2 3 |
# либо оператор pass def only_pass(): pass |
1 |
only_pass() |
Хотя может показаться, что в этом случае функция ничего не выдает, на самом деле это не так. И в том, и в другом случае функция возвращает особый тип данных None (отсутствие значения).
1 |
print(only_return()) |
1 |
None |
Функция print() вместо return
Помимо ключевого слова return, результат работы функции можно вывести с помощью print().
1 2 3 |
def double_print(x): res = x * 2 print(res) |
1 |
double_print(5) |
1 |
10 |
Хотя визуально вывод идентичен, отличие все-таки есть:
- Использование return возвращает значение функции (в нашем случае значение переменной res) и прерывает ее работу
- Функция print() просто выводит это значение пользователю и не влияет на дальнейшее исполнение кода, если он есть
Параметры собственных функций
С точки зрения параметров, у собственных функций те же самые возможности, что и у встроенных функций. В частности, параметры могут быть позиционными и именованными.
1 2 3 4 5 |
# объявим функцию с параметрами x и y, def calc_sum(x, y): # которая возвращает их сумму return x + y |
1 2 |
# вызовем эту функцию с одним позиционным и одним именованным параметром calc_sum(1, y = 2) |
1 |
3 |
Параметры собственной функции также могут быть заданы по умолчанию. В этом случае при вызове функции их указывать не обязательно.
1 2 3 4 |
def calc_sum_default(x = 1, y = 2): return x + y calc_sum_default() |
1 |
3 |
И конечно функция может изначально не иметь параметров.
1 2 3 4 5 |
# эта функция просто выводит текст 'Some string' def print_string(): print('Some string') print_string() |
1 |
Some string |
Аннотация функции
Аннотация функции (function annotation) позволяет явно прописать тип данных параметров (parameter annotation) и возвращаемых значений (return annotation).
1 2 3 4 |
# укажем, что на входе функция принимает тип float, а возвращает int # значение 3,5 - это значение параметра x по умолчанию def f(x: float = 3.5) -> int: return int(x) |
1 2 |
# желаемый тип данных можно посмотреть через атрибут __annotations__ f.__annotations__ |
1 |
{'return': int, 'x': float} |
1 2 |
# вызовем функцию без параметров f() |
1 |
3 |
Аннотация не является обязательной и никак не вляет на выполнение кода.
1 2 3 4 |
# сохраним аннотации, но изменим суть функции def f(x: float) -> int: # теперь вместо int она будет возвращать float return float(x) |
1 2 |
# вновь вызовем функцию, передав ей на входе int, и ожидая на выходе получить float f(3) |
1 |
3.0 |
Дополнительные возможности функций
Вызов функции можно совмещать с арифметическими операциями.
1 2 |
# вызовем объявленную выше функцию и умножим ее вывод на два calc_sum(1, 2) * 2 |
1 |
6 |
Доступны и логические операции.
1 |
calc_sum(1, 2) > 2 |
1 |
True |
Если результатом вывода является строка, то у этой строки также есть индекс.
1 2 3 4 5 |
def first_letter(): return 'Python' # обратимся к первой букве слова Python first_letter()[0] |
1 |
'P' |
Функция может не использовать параметры, но получать данные от пользователя через input().
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def use_input(): # запросим у пользователя число и переведем его в тип данных int user_inp = int(input('Введите число: ')) # возведем число в квадрат result = user_inp ** 2 # вернем результат return result # вызовем функцию use_input() |
Появится окно для ввода числа.

Введем число пять и посмотрим на результат.
1 2 |
Введите число: 5 25 |
Результат вызова функции
Функция не обязательно возвращает только число или строку, это может быть, в частности, список, кортеж или словарь. Рассмотрим несколько примеров.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# объявим функцию, которая на входе получает число, # а на выходе формирует список чисел от 0 до числа, # предшествующего заданному def create_list(x): # создадим пустой список l = [] # в цикле for создадим последовательность for i in range(x): # и поместим ее в список l.append(i) return l # результатом вызова этой функции будет список create_list(5) |
1 |
[0, 1, 2, 3, 4] |
Функция может возвращать сразу несколько значений.
1 2 3 4 |
def tuple_f(): string = 'Python' x = 42 return string, x |
Если результат вызова этой функции записать в две переменных, то ожидаемо в них окажется строка и число.
1 2 3 |
a, b = tuple_f() print(a, b) print(type(a), type(b)) |
1 2 |
Python 42 <class 'str'> <class 'int'> |
Если записать в одну, то получится кортеж.
1 2 3 |
c = tuple_f() print(c) print(type(c)) |
1 2 |
('Python', 42) <class 'tuple'> |
Функция может возвращать и логическое значение. Давайте объявим функцию, которая проверяет четное ли ей передали число (и в этом случае вернет True) или нечетное (и тогда False).
1 2 3 4 5 6 7 8 9 |
# проверим равен ли нулю остаток от деления на два def if_divisible(x): if x % 2 == 0: return True else: return False # попробуем с числом 10 if_divisible(10) |
1 |
True |
Использование библиотек
Внутри функций можно использовать дополнительные библиотеки Питона. Например, применим функцию mean() библиотеки Numpy для расчета среднего арифметического.
1 2 3 4 5 |
# на входе функция примет список или массив x, def mean_f(x): # рассчитает среднее арифметическое и прибавит единицу return np.mean(x) + 1 |
1 2 3 4 5 6 7 |
# перед вызовом функции нужно не забыть импортировать соответствующую библиотеку import numpy as np # и подготовить данные x = [1, 2, 3] mean_f(x) |
1 |
3.0 |
Глобальные и локальные переменные
Некоторые переменные существуют (или как еще говорят, видны) только внутри функции, другие во всей программе. В первом случае, говорят про локальные переменные, во втором — про глобальные. Сразу приведем несложный пример.
1 2 3 4 5 6 |
# создадим глобальную переменную ВНЕ функции global_name = 'Петр' # а затем используем ее внутри новой функции def show_name(): print(global_name) |
1 |
show_name() |
1 |
Петр |
Здесь никаких проблем не возникло. Мы создали глобальную переменную и потом легко использовали ее внутри очередной функции. Обратное не всегда возможно.
1 2 3 4 5 |
# а теперь вначале создадим функцию, # внутри которой объявим локальную переменную def show_local_name(): local_name = 'Алена' print(local_name) |
1 |
show_local_name() |
1 |
Алена |
Функция была вызвана без проблем. При этом если мы попробуем обратиться к переменной local_name вне этой функции, Питон выдаст ошибку.
1 |
local_name |

Все дело в том, что область видимости этой переменной ограничена функцией show_local_name(). Для того чтобы она была видна во всей программе, можно использовать ключевое слово global.
1 2 3 4 |
def make_global(): global local_name local_name = 'Алена' print(local_name) |
1 |
make_global() |
1 |
Алена |
1 2 |
# теперь ошибки быть не должно local_name |
1 |
'Алена' |
Давайте посмотрим, что будет, если есть две переменные с одинаковым названием, причем одна из них глобальная, а вторая — локальная.
1 2 3 4 5 6 7 |
# объявим глобальную переменную global_number = 5 def print_number(): # затем объявим локальную переменную local_number = 10 print('Local number:', local_number) |
Функция всегда «предпочтет» содержащуюся в ней локальную переменную.
1 |
print_number() |
1 |
Local number: 10 |
При этом значение глобальной переменной для остального кода не изменится.
1 |
print('Global number:', global_number) |
1 |
Global number: 5 |
Анонимные или lambda-функции
Функции создают не только через ключевое слово def и название функции. Можно использовать слово lambda и вообще обойтись без названия. Приведем простой пример.
1 2 3 4 5 |
# создадим функцию, которая принимает два числа и перемножает их lf = lambda a, b: a * b # вызовем функцию и передадим ей числа 2 и 3 lf(2, 3) |
1 |
6 |
Теперь давайте в деталях рассмотрим, как мы получили такой результат.

- вначале ставится ключевое слово lambda
- за ним идут передаваемые параметры
- через двоеточие пишется исполняемое выражение
Разумеется, ничто не мешает поместить этот же функционал в обычную функцию.
1 2 3 4 |
def normal_f(a, b): return a * b normal_f(2, 3) |
1 |
6 |
Анонимную функции при этом удобно использовать в тех случаях, когда применение обычной функции избыточно. Продемонстрирую на примере.
Lambda-функция внутри функции filter()
Предположим, у нас есть список чисел, и мы хотим оставить в нем только те числа, которые больше 10. Давайте решим эту задачу с помощью функции filter() и lambda-функции.
Функция filter() принимает два параметра:
- Во-первых, еще одну функцию, выполняющую роль критерия; она выдает True, если элемент нужно оставить, и False — если убрать
- Во-вторых, набор элементов, которые нужно отфильтровать в виде списка, кортежа или множества
Получив их, filter() применяет критерий (функцию) к каждому из элементов набора.
Посмотрим на реализацию на Питоне. Вначале создадим список.
1 |
nums = [15, 27, 9, 18, 3, 1, 4] |
Теперь зададим фильтрующую lambda-функцию.
1 2 3 |
# буквально мы пишем, для каждого n, # выдай True, если число больше 10, иначе - False criterion = lambda n: True if (n > 10) else False |
После этого поместим criterion и nums в функцию filter(). Так как сама функция filter() вернет нам не список, а специальный объект iterator, его в свою очередь нужно преобразовать обратно в список с помощью функции list().
1 |
list(filter(criterion, nums)) |
1 |
[15, 27, 18] |
Чаще такой функционал записывают в одну строчку.
1 |
list(filter(lambda n: True if (n > 10) else False, nums)) |
1 |
[15, 27, 18] |
И в этом и заключается удобство lambda-функции, ее не надо объявлять заранее. Через обычную функцию код выглядел бы так.
1 2 3 4 5 6 7 |
def criterion_2(n): if n > 10: return True else: return False list(filter(criterion_2, nums)) |
1 |
[15, 27, 18] |
Lambda-функция внутри функции sorted()
Теперь рассмотрим lambda-функцию в коде, который мы использовали на занятии по рекомендательным системам. Воспроизведем в миниатюре часть решения из того занятия.
В какой-то момент мы получили список из кортежей. И в каждом кортеже был индекс фильма и расстояние до него.
1 |
list_of_tuples = [(901, 0.0), (1002, 0.22982440568634488), (442, 0.25401128310081567)] |
Затем мы захотели отсортировать этот список по расстоянию, то есть по второму элементу кортежа.
Мы взяли функцию sorted() и в качестве параметра ключа, по которому сортировать список (key), передали ей lambda-функцию, принимающей кортеж из двух элементов на входе, и выдающей второй его элемент на выходе.
1 |
sorted(indices_distances, key = lambda x: x[1], reverse = True) |
1 |
[(442, 0.25401128310081567), (1002, 0.22982440568634488), (901, 0.0)] |
Параметр reverse = True, напомню, задает сортировку по убыванию.
Немедленно вызываемые функции
Lambda-функции относятся к так называемым немедленно вызываемым функциям или immediately invoked function expressions (IIFE). Это означает, что мы можем одновременно объявить и вызвать такую функцию.
1 2 |
# обратите внимание на использование скобок (lambda x: x * x)(10) |
1 |
100 |
*args и **kwargs
Прежде чем завершить, поговорим про еще одну важную тему, а именно про так называемые *args (сокращение от arguments) и **kwargs (keyword arguments).
Они позволяют передавать функции различное количество позиционных (*args) или именованных (**kwargs) аргументов.
Рассмотрим на примере. Начнем с *args.
*args
Предположим, что у нас есть простая функция, которая принимает два числа и считает среднее арифметическое.
1 2 3 4 5 6 |
# объявим функцию def mean(a, b): return (a + b) / 2 # и передадим ей числа 1 и 2 mean(1, 2) |
1 |
1.5 |
Все отлично работает, но мы не можем передать этой функции больше двух чисел. Возможным решением станет функция, которая изначально принимает список в качестве аргумента.
1 2 3 4 5 6 7 8 9 10 11 12 |
# объявим функцию, которой нужно передать список def mean(list_): # зададим переменную для суммы, total = 0 # в цикле сложим все числа из списка for i in list_: total += i # и разделим на количество элементов return total / len(list_) |
1 2 3 4 5 |
# создадим список list_ = [1, 2, 3, 4] # и передадим его в новую функцию mean(list_) |
1 |
2.5 |
Все опять же работает, но нам каждый раз нужно создавать список. При попытке передать отдельные числа функция выдаст ошибку.
1 |
mean(1, 2) |

*args позволяет передавать функции произвольное количество отдельных чисел.
1 2 3 4 5 6 |
# объявим функцию с *args def mean(*nums): total = 0 for i in nums: total += i return total / len(nums) |
1 |
mean(1, 2, 3, 4) |
1 |
2.5 |
Как вы видите, главным элементом здесь является оператор распаковки * (unpacking operator). Он принимает все передаваемые в функцию числа и формирует из них кортеж.
Затем мы проходимся по элементам этого кортежа, рассчитываем их сумму и делим на количество элементов. Использовать слово args не обязательно, мы назвали наш позиционный аргумент nums.
Если мы по какой-то причине захотим передать функции список, мы можем это сделать.
1 2 |
# передадим в функцию список mean(*list_) |
1 |
2.5 |
В этом случае мы передаем название списка со звездочкой *.
Для того чтобы продемонстрировать преобразование чисел в кортеж, напишем вот такую несложную функцию.
1 2 |
def test_type(*nums): print(nums, type(nums)) |
1 |
test_type(1, 2, 3, 4) |
1 |
(1, 2, 3, 4) <class 'tuple'> |
**kwargs
При использовании **kwargs происходит почти то же самое за тем исключением, что мы распаковываем именованные, а не позиционные аргументы. И распаковываем их в словарь, а не в список. Сразу посмотрим на примере.
1 2 |
def f(**kwargs): return kwargs.items() |
1 2 3 |
# оператор ** примет произвольное количество именованных аргументов # и внутри функции сформирует из них словарь f(a = 1, b = 2) |
1 |
dict_items([('a', 1), ('b', 2)]) |
Приведем более сложный пример. Напишем функцию, которая на вход примет произвольное количество чисел (позиционный аргумент), преобразует в кортеж (*args) и рассчитает среднее арифметическое (mean) и среднее квадратическое отклонение (standard deviation).
Для каждой из метрик мы дополнительно создадим именованный параметр, который определит выводить эту метрику или нет. Параметры мы передадим через **kwargs. Внутри функции из них будет сформирован словарь.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# nums функция преобразует в кортеж, params - в словарь def simple_stats(*nums, **params): # если ключ 'mean' есть в словаре params и его значение == True if 'mean' in params and params['mean'] == True: # рассчитаем среднее арифметическое кортежа nums и округлим # \t - это символ табуляции print(f'mean: \t{np.round(np.mean(nums), 3)}') # если ключ 'std' есть в словаре params и его значение == True if 'std' in params and params['std'] == True: # рассчитаем СКО кортежа nums и округлим print(f'std: \t{np.round(np.std(nums), 3)}') |
Вызовем функцию simple_stats() и передадим ей числа и именованные аргументы.
1 |
simple_stats(5, 10, 15, 20, mean = True, std = True) |
1 2 |
mean: 12.5 std: 5.59 |
Если для одного из параметров задать значение False, функция не выведет соответствующую метрику.
1 |
simple_stats(5, 10, 15, 20, mean = True, std = False) |
1 |
mean: 12.5 |
Для того чтобы передать параметры списком и словарем, нам нужно использовать операторы распаковки * и ** соответственно.
1 2 3 4 |
list_ = [5, 10, 15, 20] settings = {'mean' : True, 'std' : True} simple_stats(*list_, **settings) |
1 2 |
mean: 12.5 std: 5.59 |
Количество именованных аргументов в **kwargs может быть любым. Ничто не мешает нам добавить еще один параметр.
1 2 |
# добавим параметр median simple_stats(5, 10, 15, 20, mean = True, std = True, median = True) |
1 2 |
mean: 12.5 std: 5.59 |
Впрочем, для того чтобы это имело смысл, такой параметр должен быть прописан внутри функции.
В заключение скажу, что все приведенные выше примеры являются учебными и без *args и **kwargs здесь конечно можно обойтись. На практике, они применяются в более сложных конструкциях, например, в так называемых декораторах, однако эта тема выходит за рамки сегодняшнего занятия.
Подведем итог
Сегодня мы впервые поговорили про функции в программировании и выяснили, чем они отличаются от функций в математике. Кроме того мы узнали, что в Питоне можно:
- использовать готовые функции, которые уже встроены либо в базовый функционал, либо в дополнительную библиотеку;
- объявлять собственные функции через ключевое слово def и название функции; а также
- создавать анонимные или lambda-функции, которые очень удобно применять там, где в полноценных собственных функциях нет необходимости
Помимо этого, мы выяснили, что любой функции можно передать параметры и аргументы, которые в зависимости от способа передачи могут быть позиционными или именованными.
Мы узнали, что у переменной может быть локальная и глобальная области видимости.
Наконец, мы поговорили про возможность передачи различного количества позиционных и именованных аргументов через *args и **kwargs.
Вопросы для закрепления
Какие три вида функций мы изучили?
Посмотреть правильный ответ
Ответ: встроенные, собственные, а также анонимные или lambda-функции.
Какие бывают параметры и аргументы функции?
Посмотреть правильный ответ
Ответ: позиционные (в этом случае мы указываем только аргумент, но ставим его в определенном порядке) и именованные (указываем и параметр, и аргумент, но порядок не важен).
Какова область видимости локальной переменной?
Посмотреть правильный ответ
Ответ: область видимости локальной переменной ограничена той функцией, в которой эта переменная была объявлена.
В ноутбуке к лекции приведены дополнительные упражнения⧉.
В следующий раз мы подробнее рассмотрим списки, кортежи и множества.
Ответы на вопросы
Вопрос. Скажите, а почему используется символ нижнего подчеркивания в названии переменной list_?
Ответ. Не стоит использовать просто list для именования переменных, потому что это слово зарезервировано для названия функции. Аналогично не стоит использовать dict, tuple, set и т.д. При этом некоторые слова использовать в качестве названия переменных просто не получится. Питон выдаст ошибку. Это, например, if, for, True, False, import и т.п.
Символ подчеркивания является одним из вариантов решения.