# Функции
Функция — блок кода для выполнения определенной задачи, который можно использовать многократно в разных частях программы. Существует множество встроенных в Python готовых функций. Некоторые вы уже применяли ранее: print() , str() , int() , float() , len() .
Создание функции называется определение. Оно начинается с ключевого слова def (от англ. definition, «определение»), затем идут имя функции, её аргументы, или параметры в круглых скобках и двоеточие. Начиная со следующей строки, с отступом в 4 пробела от начала, записывают тело функции — код, который она выполняет. Обращение к функции называется вызов функции. Функция вызывается по имени, при вызове ей передают аргументы, с которыми она должна что-то сделать.
Напишем приветствие как функцию:
Создали функцию с именем say_hello (англ. say hello, "поздороваться"). В любом месте программы при вызове say_hello() будет выводиться на экран приветствие с текстом, зависящим от аргумента current_hour (англ. current hour , "текущий час").
# Упражнения
- На основе заготовленного кода напишите функцию print_friends_count() для вывода количества друзей. Аргументом сделайте friends_count . Вызовите эту функцию не менее трёх раз с разными аргументами. Значениями friends_count могут быть любые натуральные числа.
- Напишите цикл для запусков print_friends_count() c аргументами от 1 до 10. Проследите изменения выведенных надписей.
# Упражнения tkinter
Напишите оконное приложение выводящее список количества друзей.
Напишите оконное приложение с полем ввода количества друзей. Кнопкой при нажатии на которую выводится сообщение о количестве друзей.
# Аргументы функции
В Python аргументам функций может быть присвоено значение по умолчанию. Оно подставляется, когда при вызове функции вы не оставляете насчёт аргументов никаких инструкций.
Функция say_hello() принимает два аргумента — current_hour и name (англ. name, "имя"), которому задано значение по умолчанию. При вызове только с одним аргументом current_hour значением второго аргумента name станет пустая строка.
При вызове функции можно явно указывать, какому аргументу какое значение соответствует. В таком случае порядок следования аргументов в скобках роли не играет.
# Упражнения
Допишите код функции print_friends_count() , добавьте аргумент name со значением по умолчанию. Если вы при вызове передаёте функции имя, она должна вывести на экран строку вида '<имя>, у тебя N друзей' , если нет — тогда просто 'У тебя N друзей' .
Анфиса может анализировать списки. Например, подсчитывать дни, когда в вашем городе или в городах ваших друзей стояла хорошая погода.
Есть списки средних дневных температур в Москве за май 2017 и 2018 годов. Создайте функцию comfort_count(temperatures) для подсчёта в переданном списке комфортных дней — дней с температурой воздуха от 22 до 26 градусов включительно.
Функция в результате работы должна вывести на экран строку 'Количество комфортных дней в этом месяце: N' , где N — результат подсчёта в цикле с условием. Сначала посчитайте приятные дни в мае 2017-го года, а потом — в мае 2018-го.
# Разбиение на функции
В больших проектах, написанных на Python, принято выносить весь повторяющийся код в отдельные функции перед телом основной программы. Посмотрим на пример:
Функция say_hello() содержит основную логику, а runner() (от англ. run, "запускать") выполняет роль управляющего механизма. Такое разделение придаёт коду программы аккуратный внешний вид, делает его более выразительным, упрощает чтение.
# Упражнения
- Подготовьте код Виты к использованию на сервере.
- Напишите функцию process_query() (англ. process query, «обработать запрос»). Перенесите в неё весь код из тела основной программы. Эта функция будет принимать на вход запросы пользователя и выдавать ответ на них. Пока она может обработать всего один запрос — сообщить количество друзей.
- Добавьте вызов функции process_query() в тело основной программы.
- На серверы приходит множество запросов — от разных пользователей или от других серверов. Измените функцию process_query() (обработчик запроса), чтобы она поддерживала несколько разных запросов, а не только один.
- Добавьте аргумент query в функцию process_query() . Этот аргумент будет сообщать, какой именно запрос необходимо обработать.
- В начало функции process_query() добавьте проверку значения переменной query: если значение равно 'Сколько у меня друзей?' — выведите ответ на этот вопрос, как в предыдущем задании;
- в противном случае — выведите '<неизвестный запрос>'. Добавьте вызов process_query('Сколько у меня друзей?') в основное тело программы. Добавьте ещё один вызов process_query('Как меня зовут?') в основное тело программы.
- Доработайте программу.
- Добавьте в функцию process_query() обработку ещё одного запроса 'Кто все мои друзья?'. В ответ нужно выводить на экран Твои друзья: <список_друзей>, где <список_друзей>— строка, состоящая из списка друзей, разделённых запятой и пробелом.
- Добавьте вызов process_query('Кто все мои друзья?') в тело основной программы.
# Возврат значений из функции
Пока что все наши функции печатали результаты своей работы на экран. Но обычно задача функции не сводится к выступлениям в печати. Как правило, это код, производящий специальные вычисления. Он не выводит результаты на экран, а возвращает их как значения. Значение вернётся в вызывающий код, и там будет использовано. Например, сохранено в переменной. Если программе нужно, к примеру, несколько раз посчитать одно и то же, эти вычисления поручают особым функциям:
Ключевое слово return (англ. «возвращать») указывает, какое значение функция вернёт основному коду.
# Упражнения
- Доработаем программу подсчёта комфортных дней в мае.
- Измените функцию comfort_count() , чтобы она не печатала количество комфортных дней, а возвращала.
- Чтобы увидеть результат выполнения функции, напечатайте его в основном коде командой print(comfort_count(may_2017)) .
# Вызов функции из функции
Функцию можно вызвать не только из основного кода, но и из другой функции.
Хорошая привычка — выделять вычисления и вывод в отдельные функции, чтобы печать результатов не была разбросана по всему коду.
Если бы мы ошиблись и забыли вернуть строку text из функции show_info(), то вывод получился бы странный:
Что такое None здесь? Это специальное значение в Python, и оно обозначает. ничего (англ. none, "ничто"). Если значение какой-нибудь переменной равно None , обычно это значит, что она не определена, не имеет никакого значения. Функция runner() печатает результаты вызовов функции show_info() . Так как в ней нет return , то она ничего не возвращает, и print() печатает это самое ничего.
Функции#
Про определение функций в python можно подробно прочитать в документации по ссылке.
Объявление функции ( function definition ) состоит из заголовка и тела.
Заголовок начинается с ключевого слова def , за которым должно следовать название (имя) функции и список формальных параметров. Завершается заголовок символом двоеточия.
Далее приводится тело функции, инструкции в котором приводятся с постоянным отступом вправо относительно заголовка функции.
Первой строкой в теле функции допускается указывать документирующую строку.
Когда интерпретатор встречает определение функции, он создаёт вызываемый объект-функцию и связывает этот объект с указанным при объявлении именем. Это имя также функции сохраняется в атрибут __name__ объекта-функции.
В ячейке выше на первой строке мы объявили функцию с именем add принимающую два аргумента под именами x и y .
На следующей строке располагается документирующая строка.
Документирующая строка сохраняется в атрибут __doc__ объекта функции и также выводится в справке по функции, которую можно сгенерировать встроенной функцией help.
На последней строке функция возвращает результат вычисления выражения x + y вызывающему коду.
Управление потоком управления и возвращение значений из функции#
При вызове функции поток управления программой передаётся этой функции. Поток управления программой возвращается обратно вызывающему коду, когда исполнение функций завершается, что может произойти по трём причинам:
встречено ключевое слово return ;
достигнут конец функции;
возникло необработанное исключение, которое начало распространяться по стеку вызовов.
Последний случай разбирается позднее в разделе “ Исключения ”, разберем первые два.
Если встречается конструкция вида
т.е. ключевое слово return и некоторое выражение expression , то вызывающему коду возвращается результат вычисления expression . Именно такая ситуация и встречается в определенной выше функции add : в качестве expression выступает выражение x + y .
Если же после ключевого слова return ничего не стоит, т.е. встречается конструкция вида
то вызывающему коду возвращается значение None .
Если же функция заканчивается без ключевого слова return , то после исполнения последней инструкции тела функции вызывающему коду возвращается значение None . Т.е. следующие три определения функции эквиваленты.
Таким образом функция всегда возвращает какое-то значение, если её исполнение завершилось без возникновения исключений. Это значение явно задаётся выражением справа от ключевого слова return или возвращается None , если ключевое слово return не встретилось при исполнении тела функции или справа от оператора return ничего не указано.
Функции как объекты первого класса#
Прочитав объявление функции, интерпретатор создаёт вызываемый объект-функцию типа function и связывает его с указанным при объявлении функции именем. При этом объекты функционального типа function ничем не выделяются на фоне объектов всех остальных типов. Как и с любыми другими объектами в python можно совершать следующие операции и над функциями:
передать в качестве аргумента функции;
возвращать из функции;
В программировании такие объекты с такими свойствами принято называть объектами первого класса и далеко не во всех языках программирования функции являются таковыми. Но в python это так и давайте продемонстрируем это.
Присваивание переменной#
Присвоение переменной в python — связывание с именем. В качестве демонстрации этой возможности свяжем встроенную функцию print с именем p и вызовем её от этого имени.
Передача в качестве аргумента функции#
Передачу функции в качестве аргумента другой функции мы уже встречали на примере получения справки функцией help . Разберем пример того, как пользовательская функция может принимать в качестве параметра объект-функцию.
В ячейке выше определена примитивная функция print_function_name , которая просто печатает содержимое атрибута __name__ функции. Далее на вход этой функции сначала передаётся определенная ранее функция add . А потом на вход этой функции передаётся она сама! Да, python настолько гибкий, что функция может обработать саму себя. Правда сложно представить себе ситуацию, где такая возможность пригодится и будет лучшей из всех возможных альтернатив.
Напишем чуть более содержательную функцию apply , которая принимает на вход функцию f и список l и подменяет каждый элемент l[i] значением f(l[i]) , т.к. применяет функцию f к элементам списка l на месте.
На самом деле есть встроенная функция map, которая делает то же самое, только не на месте.
Возвращение из функции. Замыкание#
Функция может возвращать функцию из себя. Распространенное применение такого приема — декораторы (см. “ Декораторы ”). Одна из основных причин такой распространенности — возможность реализовать шаблон замыкание ( closure ).
Рассмотрим самый простой пример. Реализуем функцию, которая генерирует функцию, запоминающую момент своего создания.
Разберем, как этот пример работает. Обратим внимание на функцию make_remembering_function .
Для начала обратим внимание на то, что внутри тела этой функции объявляется другая функция remembering_function и она же возвращается в качестве результата. При каждом вызове функции make_remembering_function интерпретатор заново встречает объявление функции remembering_function , создаёт функциональный объект и связывает его с именем remembering_function в пространстве локальных имен функции make_remembering_function (каждому вызову одной и той же функции соответствует своё уникальное пространство локальных имен).
Теперь обратим внимание, что созданная функция remembering_function обращается к переменной time_of_creation , которая в этой функции не объявляется.
Такие переменные считаются свободными переменными и к списку таковых даже можно получить доступ напрямую.
Связывание свободных переменных с объектов происходит во время вызова функции. Поиск происходит в приоритете
nonlocals — пространство локальных имен замыкающей функции (в данному примере пространство локальных имен функции make_remembering_function );
globals — глобальное пространство имен модуля, в котором объявлена эта функция;
builtins — пространство встроенных имен.
Искусственно произвести процесс разрешения можно средствами модуля inspect, который предназначен для инспекции python объектов. Метод inspect.getclosurevars разрешает все имена, которые используются в функции, но не объявлены в самой функции, с объектами.
В данном примере имя time_of_creation обнаружено в пространстве имен замыкающей функции, а print в пространстве имен встроенных функций.
Рассмотрим несколько другой пример.
Этот пример работает, основываясь на тех же механизмах, что и предыдущей, так как параметры функции — локальные переменные функции. В данном примере name — локальная переменная функции make_greeter_function , и она же является переменной из локального пространства имен замыкающей функции greet (иными словами она nonlocal для функции greet ).
Опциональные и обязательные параметры функции#
Как и в C/C++ , у параметров функции могут быть значения по умолчанию ( default value ). Такие параметры ещё называют опциональными ( optional parameter ), т.к. при вызове функции их можно не указывать, а параметры без значений по умолчаний называют обязательными ( required parameter ).
В примере выше объявлена функция say_hi_n_times с обязательным параметром name и опциональным параметром n , у которого значение по умолчанию равняется 1.
Вызвать это функцию можно двумя способами:
указав оба аргумента:
указав только обязательный параметр name и опустив опциональный n :
При объявлении функции можно указывать произвольное количество как и обязательных, так и опциональных параметров, но необходимо соблюдать следующее требование: все обязательные параметры должны быть перечислены ранее всех опциональных. Т.е. допустим только следующий синтаксис.
Нарушение этого требования — синтаксическая ошибка.
Про механизм вычисления значения по умолчанию надо помнить две детали.
Во-первых, значение по умолчанию вычисляется при объявлении функции.
Здесь f() печатает строку “before”, а не “after”, потому что на момент, когда интерпретатор читал объявление функции f и создавал соответствующий объект, имя x ссылалось на объект 5 .
Во-вторых, это значение вычисляется всего один раз. Это играет роль, если значение по умолчанию — изменяемый объект.
А здесь один и тот же список разделяется между тремя вызовами функции f . Этот список создан при объявлении функции и затем используется при всех вызовах функции без второго аргумента.
Передача аргументов в функцию по имени#
Во всех предыдущих примерах аргументы передавались в функцию позиционно, т.е. то, в какой параметр попадет значение при передаче его в качестве аргумента, определялось тем, в какой позиции он был указан при вызове функции. В python можно передавать аргументы так же и по именам параметров, нарушая при этом порядок, в котором параметры перечислены в заголовке вызываемой функции.
Для этого при вызове функции указывается имя параметра, затем знак “ = ”, а затем значение, т.е. если мы хотим передать в функцию f значение value в качестве параметра с именем x , то используется следующий синтаксис.
Это сильно увеличивает количество способов передать одни и те же аргументы в функцию. Например, в случае функции с двумя параметрами существует 4 равнозначных способа её вызвать, в том числе:
передав оба аргумента позиционно;
передав первый аргумент позиционно, а второй аргумент по имени;
передав оба аргумента по имени в том же порядке, в котором они перечисленны в определении функции;
передав оба аргумента по имени в противоположном порядке, чем порядок при котором они перечислены в определении функции.
Как и в случае с опциональными параметрами в определении функции, есть ограничение на передачу параметров по имени: необходимо сначала указать все параметры, передаваемые позиционно, а только потом все параметры, передаваемые по именам.
Нарушение этого правила — синтаксическая ошибка.
Сугубо позиционные параметры и сугубо именованные параметры#
Иногда может быть удобно сделать прием каких-то параметров только по позиции, а ряда других параметров только по имени. Для этого внутри списка параметров ставятся символы “ / ” и “ * ”:
все параметры, перечисленные до символа “ / ” считаются сугубо позиционными, т.е. передавать их можно только по позиции;
все параметры, перечисленные после символа “ * ” считаются сугубо именованными, т.е. передавать их можно только по имени;
Ниже приведена иллюстрация.
Примеры некорректных вызовов функции f .
Первый два параметра должны быть переданы по позиции.
Передано 5 позиционных аргумента, а в объявлении всего 3 параметра допускающих передачу по позиции.
Перехват произвольного количества позиционных параметров.#
Python допускает синтаксис, которые позволяет перехватывать произвольное количество позиционно переданных аргументов. Для этого при объявлении функции указывается параметр с символом “ * ” перед ним. Обычно такому параметру дают имя *args (сокращение от arguments ). Ниже приведен пример такого объявления.
Вызывать такую функцию можно передавая произвольное количество параметров.
Параметры и аргументы функций#
Цель создания функции, как правило, заключается в том, чтобы вынести кусок кода, который выполняет определенную задачу, в отдельный объект. Это позволяет использовать этот кусок кода многократно, не создавая его заново в программе.
Как правило, функция должна выполнять какие-то действия с входящими значениями и на выходе выдавать результат.
При работе с функциями важно различать:
параметры — это переменные, которые используются при создании функции.
аргументы — это фактические значения (данные), которые передаются функции при вызове.
Параметры бывают обязательные и необязательные.
Необязательные (со значением по умолчанию):
В этом случае a — передавать необязательно.
Аргументы бывают позиционные и ключевые.
Независимо от того как параметры созданы, при вызове функции им можно передавать значения и как ключевые и как позиционные аргументы. При этом обязательные параметры надо передать в любом случае, любым способом (позиционными или ключевыми), а необязательные можно передавать, можно нет. Если передавать, то тоже любым способом.
Подробнее типы параметров и аргументов будут рассматриваться позже.
Для того, чтобы функция могла принимать входящие значения, ее нужно создать с параметрами (файл func_check_passwd.py):
В данном случае, у функции два параметра: username и password.
Функция проверяет пароль и возвращает False, если проверки не прошли и True если пароль прошел проверки:
При таком определении функции надо обязательно передать оба аргумента. Если передать только один аргумент, возникнет ошибка: