Функции#
Про определение функций в python можно подробно прочитать в документации по ссылке.
Объявление функции ( function definition ) состоит из заголовка и тела.
- Заголовок начинается с ключевого слова def , за которым должно следовать название (имя) функции и список формальных параметров. Завершается заголовок символом двоеточия.
- Далее приводится тело функции, инструкции в котором приводятся с постоянным отступом вправо относительно заголовка функции.
- Первой строкой в теле функции допускается указывать документирующую строку.
def имя_функции(параметр1, . , параметрN): "Документирующая строка." Первая инструкция в теле функции . Последняя инструкция в теле функции Инструкция снаружи тела функции
Когда интерпретатор встречает определение функции, он создаёт вызываемый объект-функцию и связывает этот объект с указанным при объявлении именем. Это имя также функции сохраняется в атрибут __name__ объекта-функции.
def add(x, y): """Return the sum of its arguments""" return x + y print(type(add)) print(add.__name__)
В ячейке выше на первой строке мы объявили функцию с именем add принимающую два аргумента под именами x и y .
На следующей строке располагается документирующая строка.
def add(x, y): """Return the sum of its arguments"""
Документирующая строка сохраняется в атрибут __doc__ объекта функции и также выводится в справке по функции, которую можно сгенерировать встроенной функцией help.
print(add.__doc__) print("-" * 40) help(add)
Return the sum of its arguments ---------------------------------------- Help on function add in module __main__: add(x, y) Return the sum of its arguments
На последней строке функция возвращает результат вычисления выражения x + y вызывающему коду.
def add(x, y): """Return the sum of its arguments""" return x + y
Управление потоком управления и возвращение значений из функции#
При вызове функции поток управления программой передаётся этой функции. Поток управления программой возвращается обратно вызывающему коду, когда исполнение функций завершается, что может произойти по трём причинам:
- встречено ключевое слово return ;
- достигнут конец функции;
- возникло необработанное исключение, которое начало распространяться по стеку вызовов.
Последний случай разбирается позднее в разделе “ Исключения ”, разберем первые два.
Если встречается конструкция вида
def имя_функции(): . return expression
т.е. ключевое слово return и некоторое выражение expression , то вызывающему коду возвращается результат вычисления expression . Именно такая ситуация и встречается в определенной выше функции add : в качестве expression выступает выражение x + y .
Если же после ключевого слова return ничего не стоит, т.е. встречается конструкция вида
то вызывающему коду возвращается значение None .
Если же функция заканчивается без ключевого слова return , то после исполнения последней инструкции тела функции вызывающему коду возвращается значение None . Т.е. следующие три определения функции эквиваленты.
def имя_функции(): print("Hello, world!")
def имя_функции(): print("Hello, world!") return None
def имя_функции(): print("Hello, world!") return
Таким образом функция всегда возвращает какое-то значение, если её исполнение завершилось без возникновения исключений. Это значение явно задаётся выражением справа от ключевого слова return или возвращается None , если ключевое слово return не встретилось при исполнении тела функции или справа от оператора return ничего не указано.
Функции как объекты первого класса#
Прочитав объявление функции, интерпретатор создаёт вызываемый объект-функцию типа function и связывает его с указанным при объявлении функции именем. При этом объекты функционального типа function ничем не выделяются на фоне объектов всех остальных типов. Как и с любыми другими объектами в python можно совершать следующие операции и над функциями:
- присвоить переменной;
- передать в качестве аргумента функции;
- возвращать из функции;
В программировании такие объекты с такими свойствами принято называть объектами первого класса и далеко не во всех языках программирования функции являются таковыми. Но в python это так и давайте продемонстрируем это.
Присваивание переменной#
Присвоение переменной в python — связывание с именем. В качестве демонстрации этой возможности свяжем встроенную функцию print с именем p и вызовем её от этого имени.
p = print p("Теперь я могу вызывать функцию print от имени p")
Теперь я могу вызывать функцию print от имени p
Передача в качестве аргумента функции#
Передачу функции в качестве аргумента другой функции мы уже встречали на примере получения справки функцией help . Разберем пример того, как пользовательская функция может принимать в качестве параметра объект-функцию.
def print_function_name(f): print(f.__name__) print_function_name(add) print_function_name(print_function_name)
В ячейке выше определена примитивная функция print_function_name , которая просто печатает содержимое атрибута __name__ функции. Далее на вход этой функции сначала передаётся определенная ранее функция add . А потом на вход этой функции передаётся она сама! Да, python настолько гибкий, что функция может обработать саму себя. Правда сложно представить себе ситуацию, где такая возможность пригодится и будет лучшей из всех возможных альтернатив.
Напишем чуть более содержательную функцию apply , которая принимает на вход функцию f и список l и подменяет каждый элемент l[i] значением f(l[i]) , т.к. применяет функцию f к элементам списка l на месте.
from math import sin, pi def apply(f, l): for i in range(len(l)): l[i] = f(l[i]) return x = [0, pi/6., pi/4., pi/3., pi/2.] apply(sin, x) print(x)
[0.0, 0.49999999999999994, 0.7071067811865476, 0.8660254037844386, 1.0]
На самом деле есть встроенная функция map, которая делает то же самое, только не на месте.
[0.0, 0.47942553860420295, 0.6496369390800625, 0.7617599814162892, 0.8414709848078965]
Возвращение из функции. Замыкание#
Функция может возвращать функцию из себя. Распространенное применение такого приема — декораторы (см. “ Декораторы ”). Одна из основных причин такой распространенности — возможность реализовать шаблон замыкание ( closure ).
Рассмотрим самый простой пример. Реализуем функцию, которая генерирует функцию, запоминающую момент своего создания.
from datetime import datetime from time import sleep def make_remembering_function(): time_of_creation = datetime.now() def remembering_function(): print(f"I was created at time_of_creation>") return remembering_function f1 = make_remembering_function() sleep(1) f2 = make_remembering_function() for f in (f1, f2): f()
I was created at 2022-10-04 15:15:34.360450 I was created at 2022-10-04 15:15:35.372219
Разберем, как этот пример работает. Обратим внимание на функцию make_remembering_function .
def make_remembering_function(): . def remembering_function(): . return remembering_function
Для начала обратим внимание на то, что внутри тела этой функции объявляется другая функция remembering_function и она же возвращается в качестве результата. При каждом вызове функции make_remembering_function интерпретатор заново встречает объявление функции remembering_function , создаёт функциональный объект и связывает его с именем remembering_function в пространстве локальных имен функции make_remembering_function (каждому вызову одной и той же функции соответствует своё уникальное пространство локальных имен).
Теперь обратим внимание, что созданная функция remembering_function обращается к переменной time_of_creation , которая в этой функции не объявляется.
. time_of_creation = datetime.now() def remembering_function(): print(f"I was created at time_of_creation>") .
Такие переменные считаются свободными переменными и к списку таковых даже можно получить доступ напрямую.