Функции: первое знакомство, определение def и их вызов

Курс по Python: https://stepik.org/course/100707

Этим занятием мы открываем с вами одну из ключевых тем в программировании – функции.

Что это такое? Условно, их можно представить как активный объект, который не просто хранит какие-либо данные, а выполняет заданный фрагмент программы. Ссылка на такой объект называется именем функции. Например, знакомая нам функция print выполняет вывод в консоль переданных ей данных. Имя этой функции так и можно воспринимать, как ссылку на свой объект-функцию. Действительно, если в интерактивном режиме напечатать имя print, то увидим, что оно ссылается на встроенную функцию. Чтобы активизировать программу, заложенную в объект-функцию, нужно поставить круглые скобки после имени функции:

print()

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

Итак, раз print – это ссылка на функцию, то что нам тогда мешает создать еще одну ссылку на этот же объект-функцию, через оператор присваивания:

f = print

И вызвать ее через второе имя f:

f("hello")

Как видите, все работает. Теперь функцию print можно вызывать и через f. А можно ли тогда исходному имени функции print присвоить другой объект, например, строку:

print = "это была функция print"

Да, и это нам никто не мешает делать. Теперь прежнюю функцию можно вызвать только через имя f:

f(print)

То есть, имя функции – это просто ссылка на объект-функцию и не более того. Конечно, заменять стандартные имена функций на другие, без острой необходимости не стоит. А вначале лучше совсем этого не делать. Именно этого правила и следует придерживаться.

Хорошо, мы теперь знаем, как правильно воспринимать функцию в Python. Но зачем они нужны? Разве нельзя писать программы и без них? Конечно можно. И самые первые языки программирования их даже не имели. Но писать программы без них, это все равно, что поехать в другой город на лошади, а не на автомобиле. С функциями программы становятся более модульными, а процесс программирования значительно упрощается. Так за счет чего это происходит?

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

DRY – Don’t Repeat Yourself

Как только, вы замечаете в программе повторение (дублирование) кода, значит, неверно строите ее архитектуру и структуру кода следует пересмотреть. Это очень грубая ошибка в программировании.

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

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

def <имя функции>([список аргументов]):
        оператор 1
        оператор 2
        …
        оператор N

Здесь имя функции придумывается программистом подобно именам переменных и, так как функция – это определенное действие, то ее имя следует выбирать как глагол, например:

go, show, get, set и т.п.

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

Давайте в качестве примера, зададим простую функцию, которая будет имитировать отправку письма:

def send_mail():
    text = "Уважаемый, Сергей Балакирев! Я так и не понял, что такое функция. Объясните лучше!"
    print(text)

Обратите внимание, имя функции send_mail – фраза-глагол, действие. Кроме того, два разных слова send и mail отделены символом подчеркивания. Я его записал для лучшей читаемости текста программы. И, глядя на название функции, сразу понятно, что она должна делать. Это рекомендуемый подход к программированию – называть переменные и функции понятными именами.

Тело этой функции состоит из двух команд: переменной text со строкой и функции print(). Так как вы дошли до этого урока, то должны хорошо понимать, почему эти две строчки образуют единый блок операторов тела функции. Да, у них у всех единый отступ от левого края. Здесь все также, как и в условных операторах или операторах циклов. Поэтому повторяться не буду.

Итак, мы с вами определили функцию. Но, если сейчас запустить эту программу, то в консоли ничего не увидим, то  есть, функция не сработала. Все верно, на данном этапе мы лишь определили, объявили функцию, но еще не вызывали. Как я говорил вначале, для вызова функции нужно записать ее имя и поставить круглые скобки – оператор вызова функции:

send_mail()

Теперь, при запуске программы, мы видим заветное сообщение. Обратите внимание, я записал вызов функции через две пустые строки после объявления функции. Это оформление по рекомендации стандарта PEP8. В интегрированной среде PyCharm, чтобы привести текст программы в соответствие с этим стандартом, достаточно нажать комбинацию клавиш Alt + Ctrl + L.

Эту же функцию мы можем вызвать и дважды:

send_mail()
send_mail()

Соответственно, она сработает два раза подряд. И еще, обратите внимание, мы можем вызывать функцию только после ее объявления. То есть, сначала сделать ее вызов, а потом объявить не получится, возникнет ошибка, что имя send_mail не определено. Нужно сначала объявлять функции и только потом их вызывать.

Вернем два подряд идущих вызова функции send_mail() и посмотрим в режиме отладки, как будет выполняться эта программа (показываем). Если нажать F8, то мы сразу выполним функцию. Чтобы зайти внутрь ее нужно нажать клавишу F7. А в теле функции уже можно F8. Так будет понятнее, как выполняется эта программа.

Сейчас у нашей функции нет никаких параметров. Давайте добавим один с именем отправителя:

def send_mail(from_name):
    text = f"""Уважаемый, Сергей Балакирев! 
Я так и не понял, что такое функция. 
Объясните лучше!
Ваш, навсегда {from_name}!"""
    print(text)

Для этого в круглых скобках записываем параметр с именем from_name (это имя мы придумываем сами, я решил назвать так) и, затем, в многострочной F-строке мы добавим это имя в конце сообщения.

Теперь функцию send_mail() следует вызывать с одним аргументом и по нашей задумке этот аргумент должен быть строкой, поэтому, запишем вызов, так:

send_mail("Иван Иванович")

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

Во-первых, обратите внимание на терминологию. Определение внутри функции называется параметром, а передаваемое значение при вызове функции – аргументом. В дальнейшем, я буду пользоваться этими словами именно в этом смысле. Во-вторых, когда происходит вызов функции, то параметр ссылается на переданный аргумент. В итоге, в теле функции from_name принимает значение "Иван Иванович", которое и подставляется в шаблон сообщения.

Давайте посмотрим, как это происходит в режиме отладки (показываем).

А что будет, если теперь попытаться вызвать эту же функцию без аргументов, так как мы это делали вначале? (Убираем комментарий). Здесь уже возникает ошибка, что функций ожидает один аргумент, а мы ничего не передаем. То же самое будет, если передать больше аргументов, например, два:

send_mail("Иван Иванович", "Сергей Балакирев")

То есть, функции нужно передавать ровно столько аргументов, сколько параметров в ней определено. Давайте пропишем второй параметр – возраст отправителя:

def send_mail(from_name, old):
    text = f"""Уважаемый, Сергей Балакирев! 
Я так и не понял, что такое функция. 
Объясните лучше!
Ваш, навсегда {from_name}! И не судите строго, мне всего {old} лет."""
 
    print(text)

Как видите, параметры записываются в круглых скобках через запятую и принимают значения аргументов при вызове функции:

send_mail("Иван Иванович", 7)
send_mail("Иван Иванович", "Сергей Балакирев")

Причем, на первый аргумент ссылается первый параметр, а на второй – второй. То есть, порядок здесь имеет значение. Также, смотрите, при втором вызове у нас вместо числа указана строка. Параметру все равно на какой тип данных ссылаться, так как в Python используется динамическая типизация (мы об этом ранее говорили). В итоге, второе сообщение несколько бессмысленно, но, тем не менее, все сработало без ошибок.

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

Курс по Python: https://stepik.org/course/100707

Видео по теме