Функции в Python
Функция в программировании представляет собой обособленный участок кода, который можно вызывать, обратившись к нему по имени, которым он был назван. При вызове происходит выполнение команд тела функции.
Функции можно сравнить с небольшими программками, которые сами по себе, то есть автономно, не исполняются, а встраиваются в обычную программу. Нередко их так и называют - подпрограммы. Других ключевых отличий функций от программ нет. Функции также при необходимости могут получать и возвращать данные. Только обычно они их получают не с ввода, а из вызывающей программы. Сюда же они возвращают результат свой работы.
Существует множество встроенных в язык программирования функций. С некоторыми такими в Python вы уже сталкивались. Это print()
, input()
, int()
, float()
, str()
, type()
. Код их тела нам не виден, он где-то "спрятан внутри языка". Нам же предоставляется только интерфейс - имя функции.
С другой стороны, программист может определять свои функции. Их называют пользовательскими. В данном случае под "пользователем" понимают программиста, а не того, кто использует программу.
Определение функции. Оператор def
В языке программирования Python функции определяются с помощью оператора def
. Рассмотрим код
Это пример определения функции. Как и другие сложные инструкции вроде условного оператора и циклов, функция состоит из заголовка и тела. Заголовок оканчивается двоеточием и переходом на новую строку. Тело имеет отступ.
Ключевое слово def сообщает Python, что перед ним определение функции. За def
следует имя функции. Оно может быть любым, также как и идентификатор, например, переменная. В программировании желательно давать всему осмысленные имена.
После имени функции ставятся скобки. В приведенном примере они пустые. Это значит, что функция не принимает никакие данные из вызывающей ее программы. Однако она могла бы их принимать, и тогда в скобках были бы указаны так называемые параметры (еще их называют аргументами функции).
После двоеточия следует тело, содержащее инструкции, которые выполняются при вызове функции. Следует различать определение функции и ее вызов. В программном коде они не рядом и не вместе. Можно определить функцию, но ни разу ее не вызвать. Нельзя вызвать функцию, которая не была определена. Определив функцию, но ни разу ее не вызвав, вы никогда не выполните ее тела.
Вызов функции
Рассмотри следующий код
После вывода на экран сообщения, осуществляется вызов функции, который выглядит просто как упоминание ее имени со скобками. Поскольку в функцию мы ничего не передаем, скобки опять же пустые. В приведенном коде функция вызывается два раза.
Когда функция вызывается, поток выполнения программы переходит к ее определению и начинает исполнять ее тело. После того, как тело функции исполнено, поток выполнения возвращается в основной код в то место, где функция вызывалась. Далее исполняется следующее за вызовом выражение.
В языке Python определение функции должно предшествовать ее вызовам. Это связано с тем, что Python читает код строка за строкой и о том, что находится "ниже по течению", ему еще неизвестно. Поэтому если вызов функции предшествует ее определению, то возникает ошибка.
Возврат значений из функции. Оператор return
Функции могут передавать какие-либо данные из своих тел в основную ветку программы. Говорят, что функция возвращает значение. В большинстве языков программирования, в том числе в Python, выход из функции и передача данных в то место, откуда она была вызвана, выполняется оператором return
.
Если Python, выполняя тело функции, встречает return, то он "забирает" значение, указанное после этой команды, и "уходит" из функции.
В данной программе в основную ветку из функции возвращается значение переменной result
. Не сама переменная, а ее значение, в данном случае - какое-то число, полученное в результате вычисления площади цилиндра.
В основной ветке программы это значение присваивается переменной answer. То есть выражение answer = getSum()
выполняется так:
вызывается функция
getSum()
;из нее возвращается значение;
это значение присваивается переменной
answer
.
Не обязательно присваивать результат переменной, его можно сразу вывести на экран:
Здесь число, полученное из getSum()
, непосредственно передается функции print()
. Если мы в программе просто напишем getSum()
, не присвоив полученные данные переменной или не передав их куда-то дальше, то эти данные будут потеряны. Но ошибки не будет.
В функции может быть несколько операторов return
. Однако всегда выполняется только один из них - тот, которого первым достигнет поток выполнения. Допустим, есть функция, которая принимает целое положительное число из консоли и возвращает квадрат этого числа. Если пользователь ввел число меньше 0
, происходит выход из функции без всяких вычислений и передачи значения
Если ввести число меньше нуля, то в консоли будет выведено
"Пустой" оператор return вернул слово None
. Это специальный тип данных - "ничто".
Когда после return ничего не указывается, то по умолчанию считается, что там стоит None
. Мы можем написать return None
. Более того, если в функции не указан оператор return
, функция все равно возвращает None
. В Python любая функция что-то возвращает.
В Python можно возвращать несколько значений, перечислив их через запятую после команды return
Возможность такого группового присвоения - особенность Python, не характерная для других языков.
Параметры и аргументы функции
В программировании функции могут не только возвращать данные, но также принимать их, что реализуется с помощью так называемых параметров, которые указываются в скобках в заголовке функции. Количество параметров может быть любым.
Параметры представляют собой переменные, которым присваиваются значения в момент вызова функции. Конкретные значения, которые передаются в функцию при ее вызове, называются аргументами.
Когда функция вызываются, ей передаются аргументы. Когда Python переходит к функции, чтобы начать ее исполнение, он присваивает переменным-параметрам переданные в функцию значения-аргументы. В примере переменной a
будет присвоено 100
, переменной b
будет присвоено 12
.
Обратим внимание еще на один момент. Количество аргументов и параметров совпадает. Нельзя передать три аргумента, если функция передает только два. Нельзя передать один аргумент, если функция требует два обязательных. В примере выше они обязательны.
Однако в Python у функций бывают параметры, которым уже присвоено значение по умолчанию. В таком случае, при вызове можно не передавать соответствующие этим параметрам аргументы. Хотя можно и передать. Тогда значение по умолчанию заменится на переданное.
Согласно правилам синтаксиса Python, при определении функции параметры, которым присваивается значение по умолчанию, должны следовать (находиться сзади) за параметрами, не имеющими значений по умолчанию.
А вот при вызове функции, можно явно указывать, какое значение соответствует нашему параметру. В этом случае их порядок не играет роли
В данном случае оба вызова - это вызовы с одними и теми же аргументами-значениями. Просто в первом случае сопоставление параметрам-переменным идет в порядке следования. Во втором случае - по ключам, которыми выступают имена параметров.
В Python определения и вызовы функций имеют и другие нюансы, рассмотрение которых мы пока опустим, так как они требуют более глубоких знаний, чем у нас есть на данный момент
Локальные и глобальные переменные
В программировании особое внимание уделяется концепции о локальных и глобальных переменных, а также связанное с ними представление об областях видимости.
Локальные переменные видны только в локальной области видимости, которой может выступать отдельно взятая функция. Глобальные переменные видны во всей программе.
"Видны" - значит, известны, доступны. К ним можно обратиться по имени и получить связанное с ними значение
К глобальной переменной можно обратиться из локальной области видимости. К локальной переменной нельзя обратиться из глобальной области видимости, потому что локальная переменная существует только в момент выполнения тела функции. При выходе из нее, локальные переменные исчезают, компьютерная память, которая под них отводилась, освобождается. Когда функция будет снова вызвана, локальные переменные будут созданы заново.
Рассмотрим следующий код
Сколько здесь переменных? Какие из них являются глобальными, а какие - локальными?
Здесь пять переменных. Глобальной является только figure
. Переменные a
и b
из функции rect()
, а также a
и h
из triangle()
- локальные. При этом локальные переменные с одним и тем же идентификатором a
, но объявленные в разных функциях - разные переменные.
Следует отметить, что идентификаторы rect
и triangle
, хотя и не являются именами переменных, а представляют собой имена функций, также имеют область видимости. В данном случае они глобальны, так как функции объявлены непосредственно в основной ветке программы.
В приведенной программе к глобальной области видимости относятся заголовки объявлений функций, объявление и присваивание переменной figure
, конструкция условного оператора.
К локальной области видимости относятся тела функций. Если, находясь в глобальной области видимости, мы попытаемся обратиться к локальной переменной, возникнет ошибка.
Однако мы можем обращаться из функций к глобальным переменным
В данном случае из тел функций происходит обращение к имени figure
.Так как переменная figure была объявлена в глобальной области видимости, она видна во всей программе.
Созданные функции не идеальны. Они должны вычислять площади фигур, но выводить результат на экран им не следовало бы. Вполне вероятна ситуация, когда результат нужен для внутренних нужд программы, для каких-то дальнейших вычислений, а выводить ли его на экран - вопрос вне компетенции функций.
Если функции будут только вычислять результат, то его надо где-то сохранить для дальнейшего использования. Давайте на секунду представим, что мы не знаем, что функции возвращают значения с помощью оператора return
и попробуем создать глобальную переменную и сохранить результат там.
Итак, мы ввели в программу глобальную переменную result
и инициировали ее нулем. В функциях ей присваивается результат вычислений. В конце программы ее значение выводится на экран.
Как видим, программа работает некорректно - в глобальную переменную result
значение записано не было.
Дело в том, что в Python присвоение значения переменной совмещено с ее объявлением. Поэтому, когда имя result
впервые упоминается в локальной области видимости, и при этом происходит присваивание ей значения, то создается локальная переменная result
. Это другая переменная, локальная, никак не связанная с глобальной переменной result
.
Когда функция завершает свою работу, то значение локальной переменной result
теряется, а глобальная не была изменена.
Когда в предыдущем примере мы вызывали внутри функции переменную figure
, то ничего ей не присваивали. Наоборот, мы запрашивали ее значение. Python искал ее значение сначала в локальной области видимости и не находил. После этого он шел в глобальную область видимости и находил ее.
В случае с result
он ничего не ищет. Он выполняет вычисления справа от знака присваивания, создает локальную переменную result
, связывает ее с полученным значением.
На самом деле можно принудительно обратиться к глобальной переменной. Для этого существует ключевое слово global
В таком варианте программа будет работать правильно.
Менять значение глобальных переменных в теле функции - плохая практика. В больших программах программисту трудно отследить, где, какая функция и почему изменила их значение. Программист смотрит на исходное значение глобальной переменной и может подумать, что оно остается таким же и ему сложно заметить, что какая-то функция поменяла его. Подобное ведет к логическим ошибкам.
Чтобы избавиться от необходимости использовать глобальные переменные, для функций существует возможность возврата результата своей работы в основную ветку программы. И уже это полученное из функции значение можно присвоить глобальной переменной в глобальной области видимости. Это делает программу более понятной.
Функции придают программе структуру
Польза функций не только в возможности многократного вызова одного и того же кода из разных мест программы. Не менее важно, что благодаря им программа обретает истинную структуру. Функции как бы разделяют ее на обособленные части, каждая из которых выполняет свою конкретную задачу.
Представим, что надо написать программу, вычисляющую площади разных фигур. Пользователь указывает, площадь какой фигуры он хочет вычислить. После этого он вводит исходные данные, например, длину и ширину в случае прямоугольника. Чтобы разделить поток выполнения на несколько ветвей, следует использовать оператор if-elif-else
:
Здесь нет никаких функций. Напишем вариант с функциями:
Он кажется сложнее, а каждая из трех функций вызывается всего один раз. Однако из общей логики программы как бы убраны и обособлены инструкции для нахождения площадей.
Программа теперь состоит из отдельных "кирпичиков". В основной ветке мы можем комбинировать их как угодно. Она играет роль управляющего механизма.
Если нам когда-нибудь захочется вычислять площадь треугольника по другой формуле, то нам не нужно будет искать код во всей программе (представьте, что она состоит из тысяч строк кода как реальные программы). Мы пойдем к месту определения функции и изменим тело одной из них.
Если понадобится использовать эти функции в какой-нибудь программе, то мы сможем импортировать их туда, сославшись на данный файл с кодом.
Last updated