[Перевод] Что можно рассказать о функциях Python на примере кофемашины?
Поделюсь с вами одной из моих любимых аналогий.
Я раньше сам молол себе кофе. Купил такую старомодную ручную кофемолку с металлической воронкой, крутильной рукояткой и маленьким деревянным подносом, на который ссыпается смолотый кофе.
Может быть, где-то у меня она ещё валяется.
Теперь я слишком занят. Перешёл на кофемашину. Она из таких, которые заправляются капсулами — то есть, максимально потворствует лени, а значит, работает максимально эффективно. Привожу этот пример на лекциях по программированию для начинающих — когда объясняю, что при работе над кодом леность — это хорошо, она свидетельствует об эффективности.
В этой статье речь пойдёт именно о такой кофемашине. Не о самом кофе, поэтому я обойдусь без избитых шуток вроде «программист — это человек, преобразующий кофеин в код».
Но ещё эта статья рассказывает о функциях Python. Она в основном о функциях Python.
Так что можете налить себе чашечку кофе — а, кажется, я обещал обойтись без глупых шуток? Извините! — и присаживайтесь поудобнее, слушайте. Возможно, в итоге у вас в голове сварится парочка свежих озарений о том, что можно сделать с функциями на Python — по капельке, по глоточку. [Стивен, стоп!]
Кофемашина. Функция
У кофемашины есть назначение — или, если хотите, функция. Здесь я буду использовать слово «функция» именно в таком общеупотребительном значении.
Причём, у кофемашины есть ровно одна функция — варить кофе.
Прежде, чем выпить вкусного кофе, вы должны предоставить кофемашине некоторые ресурсы, а именно:
- Воду
- Электричество
- Кофе (допустим, в таблетках, именно такая у меня машина)
Добавляете три этих ингредиента и нажимаете ON. Только не забудьте поставить под носик чашку или кружку.
В машине что-то происходит. Вас не особенно интересует, что именно. Вам нужно только чёрное золото, льющееся вам в чашку. Мммм вкусно.
Вы только что вызвали функцию. Кофемашина — это функция. Сейчас я употребил слово «функция» именно в том значении, которое знакомо программистам.
Вы передаёте кофемашинной функции воду, электричество и кофе в качестве аргументов. Вы вызываете функцию, когда нажимаете кнопку ON, и кофемашина возвращает жидкий кофе вам в чашку.
Но давайте разовьём эту аналогию. Здесь ещё много что можно исследовать.
Вы сами собрали себе кофемашину?
Вы купили кофемашину, распаковали её, но начинается её существование не с этого.
Кто-то когда-то собрал эту кофемашину. Инженер её спроектировал, предусмотрел, что будет у неё внутри, и как различные её элементы будут взаимодействовать друг с другом. В данном случае спроектировать и собрать кофемашину — всё равно, что определить функцию в Python:
Но, подбирая названия для функций в Python, лучше именовать их исходя из того, что они делают, а не что собой представляют. Так что давайте переименуем эту функцию:
Она по-прежнему представляет кофемашину. Но теперь мы чётко артикулируем, что нас интересует акт приготовления кофе, а не сама машина — так и должно быть!
Дорогой читатель, если вы — не мастер по ремонту кофемашин, то, вероятно, вы и не знаете, как кофемашина работает, и вас это не интересует. Налил воды, заправил кофейную таблетку, включил машину в розетку. Потом нажал кнопку — и ваш кофе готовится.
Одно дело — определять функцию, а другое — вызывать её.
Именно здесь вам и пригодится инструкция к кофемашине. В случае с функцией такую роль может играть строка docstring или страница с документацией. Здесь аналогия тоже удачная. Многие пользователи не читают инструкций — и документацию к коду обычно тоже никто не читает!
В инструкции к кофемашине немного подробнее описано, как следует пользоваться этой функцией. Чтобы кофемашина работала, вы должны предоставить её три вещи:
Кроме того, эта инструкция сообщает, что готовый продукт кофемашины — это заварной кофе. Надеюсь, это действительно так!
Итак:
- Если вы мастер по кофемашинам, то должны знать, что записать в определение функции. Это текст
def make_coffee():
- Если вы просто хотите чашку кофе, то вам достаточно знать, как обращаться с кофемашиной. Вы должны вызвать
make_coffee()
.
Когда пишешь код, вызывать функции приходится чаще, чем определять. А если вы как я, то не представляете, что находится внутри большинства из тех функций, что вы вызываете. Знаете, как выглядит код, заключённый в def print():?
Я тоже не знаю…
Не приходится задумываться об определениях функций, если вы знаете, что делают эти функции, и как их использовать.
Но зачастую, когда вы пишете код, вы выступаете в роли инженера, конструирующего кофемашины. Вам приходится определять функции. Определения функций — мощный инструмент, необходимый каждому программисту для эффективной работы.
Как сделать чашку кофе
Хорошо. У вас есть кофемашина, и вы прочитали инструкцию. Вы готовы этой машиной пользоваться.
У нас в определении функции три параметра: water, electricity
и coffee
. Эти параметры стоят на месте тех составляющих, которые понадобятся вам для работы.
Параметр water
обозначает встроенную в кофемашину ёмкость с водой. Туда наливают воду.
Шнур с вилкой, выходящий из задней стенки машины, называется electricity
. Как вы уже догадались, та ячейка, в которую закладывается кофейная таблетка, называется coffee
.
Но, пользуясь кофемашиной, вы сами должны обеспечить её водой, электричеством и кофейными таблетками. То есть, передать аргументы функции. Эти аргументы сохраняются при помощи имён параметров.
Давайте вызовем функцию make_coffee()
:
Тогда как имена параметров water, electricity
и coffee
— это просто метки, используемые при определении функции, аргументы tap_water, electricity_from_wall_socket
и blue_espresso_intenso_pod
— это конкретные сущности, которые вы применяете при вызове функции, то есть, когда включаете кофемашину.
Можно передавать функции и другие вещи. Например, вы можете обзавестись фильтрами для воды, в таком случае передаёте filtered_water
в качестве первого аргумента. Может быть, у вас сейчас проблемы с водопроводом — в таком случае, можно передать bottled_water
(бутилированную воду). А вот проточной водой sparkling_water
лучше не пользоваться!
А что, если у вас есть электровелосипед, который в стационарном виде может работать в качестве генератора электричества? В таком случае можно передать вторым аргументом electricity_from_stationary_bike
, поэтому за каждой варкой кофе вы заодно успеете неплохо размяться.
Не любите эспрессо? Тогда попробуйте caramel_pumpkin_spice_vanilla_latte_pod
(кстати, кто-нибудь пил такое?)
Можно передать любой аргумент, если это аргумент подходящего типа. В предыдущем предложении слово «тип» — это не термин. В языке Python применяется утиная типизация, поэтому в данном случае важен не сам тип, а его релевантные характеристики.
Например, в качестве первого аргумента можно передать кофемашине любую неядовитую жидкость, которая бы не разъела ёмкость. Можно. Но не значит, что следует.
В качестве второго аргумента подойдёт любая сущность, позволяющая толкать электроны с нужной энергией по проводам в машине.
Включаем машину
На кофемашине есть кнопка ON. Чтобы машина заработала, её нужно нажать. Думаю, это понятно и без инструкции по применению.
Что служит кнопкой ON в функции Python? Это скобки, которые вы добавляете вслед за именем функции, выполняющей эту задачу.
Напомню, что функция make_coffee
представляет кофемашину. Как я уже говорил, в данном примере мы вполне могли бы назвать эту функцию coffee_machine
, но назвали make_coffee
в соответствии с наилучшими практиками именования.
В коде Python есть существенная разница между make_coffee
и make_coffee()
. Первый вариант, make_coffee
— это имя функции. Оно относится к кофемашине как таковой. Рассмотрим следующую строку на Python:
Приведу ещё одну аналогию, которой часто пользуюсь. Оператор присваивания описывает, как вы присваиваете объект переменной с некоторым именем, ставя между ними знак равенства. В таком случае вы как будто кладёте объект Python в коробку. В данном случае коробка называется output
. Что же попадает в эту коробку?
Вся кофемашина!
Поскольку make_coffee
обозначает кофемашину, вы храните всю кофемашину в коробке output
. Функция в Python является объектом. Следовательно, функция хранится в переменной output
.
Теперь рассмотрим следующую строку:
Да, вы правы! Это не сработает. Но наберитесь терпения и дочитайте абзац. Скобки в данной функции работают как кнопка ON. Вы включили кофемашину, но забыли о трёх важных вещах. Во-первых, в ёмкости нет воды. Из-за этого можно было бы повредить машину, но, вдобавок, вы и не включили её в розетку, поэтому совсем ничего не произойдёт.
Читаете инструкцию. Выясняете, что необходимо передать три аргумента. Что ж, попробуем ещё раз:
Скобки на месте. Следовательно, вы включаете машину, но на этот раз вы также позаботились о воде и электричестве, а также заправили в машину кофейную таблетку.
Машина начинает тарахтеть и урчать, после чего возвращает горячий жидкий кофе, который…
…хранится в чашке под названием output
. То есть, возвращаемое значение сохраняется в переменной output
.
Не забудьте о чашке
Если хотите сохранить возвращаемое значение — не забудьте присвоить его переменной. Рассмотрим следующий код:
Вы вызываете функцию. Скобки на месте. Все три аргумента правильные.
Кофемашина начинает ворчать. Уже через полсекунды чувствуется аромат кофе. Буквально. Кофе течёт…
…прямо в сток в нижней панели машины. Вы забыли подставить чашку под носик. Кстати, и я на этой неделе тоже так прокололся — совершенно очевидно, потому что мне требуется чашечка кофе!
Если только вы не собирались скрючиться, подставить рот под носик и таким образом выпить порцию кофе — не рекомендую так делать — то с тем же успехом кофе можно было бы вылить в раковину.
Вот почему необходимо присваивать возвращаемое значение переменной. На этот раз давайте назовём переменную cup
:
Может быть, у вас есть и другая функция, drink_beverage()
. Логично предположить, что и этой функции требуется аргумент — безалкогольный напиток. Поэтому вы могли бы вызвать drink_beverage(cup)
.
Но можно вызвать функцию и вот так:
Вы вызываете make_coffee()
прямо внутри скобок функции drink_beverage()
. Когда make_coffee()
возвращает значение, оно поступает прямо в drink_beverage()
в качестве её аргумента.
Да, это как раз тот сценарий, когда вы подсовываете рот прямо под носик кофемашины.
Внимание: так вы определённо ошпарите себе рот!
Допиваем
Мне нравится аналогия с кофемашиной. Она идеально соответствует законам аналогий от Стивена. Вот эти законы:
- Аналогия должна выстраиваться на основе будничного сценария, который понятен любому без всякого интеллектуального напряжения.
- Компоненты аналогии и те концепции, которые она описывает, должны максимально точно соответствовать друг другу.
Любой из нас видел кофемашину в действии — значит, первое условие выполняется. А эта статья, надеюсь, убедила вас, что данная аналогия очень хорошо описывает основы работы с функциями — соответственно, и второе условие можно отметить галочкой.
Кроме того, для этой аналогии, как и для любых других, верно одно правило: все аналогии на каком-то этапе перестают работать. Поэтому на примере кофемашин невозможно объяснить все тонкости работы с функциями в Python. Поэтому налейте кофе и почитайте об этом в книге!