article banner

Функції на Python

Це фрагмент книги Python з нуля, яка допоможе вам навчитися програмуванню з нуля. Ви можете знайти його на Allegro, Empik та в інтернет-книгарнях.

Вибір частини коду

Часто в програмуванні багато разів виконуються однакові або подібні операції. Цілком природно, що замість того, щоб писати їх знову і знову, ми би писали частину коду, що повторюється, лише один раз, а потім викликати його, коли забажаємо. Тут у нагоді стають функції.

Щоб краще їх зрозуміти, уяви Джека, який дуже любить бургери. Він їсть їх на сніданок, обід і вечерю. Однак, щоб з’їсти такий бургер, потрібно зробити кілька дій: обсмажити м’ясо, підсмажити булочку, додати помідор і, нарешті, спожити готовий бургер. Його день можна описати так:

print("Встань із ліжка")
print("Почисть зуби")
print("Підсмаж м’ясо")
print("Підсмаж булочку")
print("Додай помідор")
print("З’їж бургер")
print("Працюй")
print("Підсмаж м’ясо")
print("Підсмаж булочку")
print("Додай помідор")
print("З’їж бургер")
print("Працюй")
print("Підсмаж м’ясо")
print("Підсмаж булочку")
print("Додай помідор")
print("З’їж бургер")
print("Пограй у гру")
print("Іди спати")

Повторення процесу приготування та споживання бургера не тільки стомлює, але й затьмарює картину того, як насправді виглядає день Джека. Ми можемо виправити цей код, утворивши функцію make_and_eat_hamburger. Її тіло міститиме окремі кроки для приготування та споживання бургера, тому ми зможемо запустити усі ці кроки, викликавши цю функцію.

# Визначення функції
def make_and_eat_hamburger():
    # Тіло функції
    print("Підсмаж м’ясо")
    print("Підсмаж булочку")
    print("Додай помідор")
    print("З’їж бургер")


print("Встань із ліжка")
print("Почисть зуби")
make_and_eat_hamburger()  # Використання функції
print("Працюй")
make_and_eat_hamburger()  # Використання функції
print("Працюй")
make_and_eat_hamburger()  # Використання функції
print("Пограй у гру")
print("Іди спати")

Стандартна функція починається зі слова def 1, далі йде її назва, круглі дужки, які можуть містити визначення параметрів (про них ми поговоримо згодом), і, нарешті, двокрапка, після якої з наступного рядка починається тіло функції. У тілі функції ми можемо використовувати все, чого ми навчилися досі: змінні, цикли чи навіть самі функції. Перед інструкцією в тілі функції має стояти відповідний відступ, оскільки саме він позначає, що є частиною тіла, а що — ні.

Щоб викликати функцію, ми використовуємо її назву та звичайні дужки. Коли функція викликається, послідовно запускаються інструкції, які містяться в її тілі. Виклик функції, як і присвоєння значення змінній або виклик функції print, — це інструкція. Зрештою print — це також функція, а print("а") — виклик функції.

def print_a():
    print("a")


def print_a_and_b():
    print_a()
    print("b")


print_a()  # a
print_a_and_b()
# a
# b
print_a()  # a

Використання функції для приховати кількох операцій під однією назвою має величезне значення. Подумай про будь-яку дію, наприклад похід у магазин. Що ти робиш? Взуваєшся, одягаєш куртку, виходиш із дому... але навіть кожна з цих операцій складається із багатьох менших. Візьмімо взування. Ми одягаємо взуття на ногу, зав’язуємо шнурівки... А що таке зав’язування шнурівок? Це скоординована послідовність багатьох рухів. Ми думаємо про абстрактні дії, за якими стоїть багато менших процесів. У програмуванні ці функції використовуються, щоб приховати довгу послідовність дій за однією назвою.

# ...

def wear_shoes():
    put_on_shoe()
    tie_shoelace()
    # ...

def go_to_store():
    wear_shoes()
    put_on_jacket()
    leave_house()
    # ...

Функція може представляти навіть дуже складні дії, залишаючись простою, оскільки використовує дещо простіші елементи. Саме так побудовані програми.

Як працюють функції?

Розгляньмо крок за кроком, як працюють функції. Програма виконує чергові інструкції рядок за рядком. Коли справа доходить до визначення функції, вона запам’ятовує її, але не викликає її тіло. Ми викликаємо тіло тоді, коли функція використовується. Отже, викликаючи функцію, ми переходимо на початок її тіла і з цього місця рухаємося далі. Коли ми підходимо до кінця тіла (або до слова return, про яке ми поговоримо незабаром), ми повертаємося до місця, в якому була викликана функція, та продовжуємо звідти. Якщо коротко, то так працюють функції. Нижче наведено приклади функцій із номерами, які показують порядок, у якому вони перелічені.

def first_function():
    print("3")  # 3
    print("4")  # 4


def second_function():
    print("6")  # 6
    print("7")  # 7


print("1")  # 1
print("2")  # 2
first_function()
print("5")  # 5
second_function()
print("8")  # 8

# Буде виведено по черзі 1 2 3 4 5 6 7 8

Варто витратити трохи часу і спробувати уявити, як наша програма рухається рядок за рядком, переходить до функції, а потім повертається туди, де її було викликано.

Називання функцій

У Python назви функцій повинні відповідати правилам snake_case, тобто всі слова мають бути записані в нижньому регістрі та розділені підкресленням _. Ця конвенція встановлена розробниками мови, її дотримується більшість програмістів. Ми з нею вже знайомі, адже використовуємо її, називаючи змінні.

Назва змінної визначає об’єкт, на який ця змінна вказує. Найчастіше це іменник (name, user, result). Назва функції вказує, що має статися під час її виклику. Тому найчастіше це дієслово (cheer, go_to_store, print).

Параметри та аргументи функцій

Функції, які ми викликали вище, завжди працювали однаково. Втім вони мають досить обмежене використання. Набагато частіше ми хочемо, щоб функція виконувала певні операції, але лише для специфічних значень. Подумайте про функцію print. Якби ми не змогли передати значення для виведення на екран, ця функція мала би небагато користі. Щоб наші функції могли набувати потрібного нам значення, достатньо у визначенні функції визначити змінні в дужках (просто вказуємо їхні назви). Такі змінні називаються параметрами. Вони визначають, що потрібно передати певній функції. З іншого боку, викликаючи функцію, ми повинні вказати значення, які будуть надані цим параметрам. Такі значення називаються аргументами. У прикладі нижче who_to_cheer — це параметр функції, а "Читачу", "Усі" та 42 — це значення, які використовуються як аргументи.

def cheer(who_to_cheer):
    print(f"Хей, {who_to_cheer}!")


cheer("Читачу")  # Хей, Читачу!
cheer("Усі")  # Хей, Усі!
cheer(42)  # Хей, 42!

Функція може мати більше параметрів, а виклик може складатися з більшої кількості аргументів, тоді ми розділяємо їх комами.

Аргументів (значень у дужках під час виклику) має бути рівно стільки, скільки визначено параметрів (змінних у дужках у визначенні). Якщо ми зробимо помилку, виникне виняток, який припинить роботу нашої програми. На щастя, цей виняток повідомить нам, де ми помилилися, і ми зможемо легко це виправити.

def b(x):
    print(x)


b()
#  Traceback (most recent call last):
#    File "<input>", line 1, in <module>
#  TypeError: b() takes exactly 1 argument (0 given)
print("Це не буде виведено")

Вправа: Функції

Напиши функцію, яка...

  • виводить суму двох чисел, переданих як аргументи;
  • виводить послідовні числа від a до b, де a і b — це параметри функції (припускаємо, що значення a < b);
  • виводить визначену кількість зірочок в одному рядку (найпростіший спосіб зробити це — створити змінну з порожнім рядком, потім виконати цикл, щоб додати більше зірочок до цієї змінної, і, нарешті, надрукувати цю змінну);
  • виводить квадрат із зірочок вказаного розміру (пам’ятай, що Ти можеш використати функцію print_stars з попереднього завдання);
  • друкує рівнобедрений трикутник заданого розміру із зірочок.

Приклади використання нижче.

print_sum(1, 2)  # 3
print_sum(3, 4)  # 7
print_sum(20, 10)  # 30

print_numbers(2, 4)
# 2
# 3
# 4

print_stars(3)  # ***
print_stars(5)  # *****
print_stars(8)  # ********

print_square(2)
# **
# **

print_square(3)
# ***
# ***
# ***

print_triangle(3)
# *
# **
# ***

print_triangle(4)
# *
# **
# ***
# ****

Відповіді в кінці книги.

Результат функції

З функціями ви знайомі з уроків математики. Там вони вивчалися для обчислення результату для заданих вхідних значень. У школі ми вивчали функції утворення квадратного степеня, модуля чи факторіала. Усі вони повертали значення, створені шляхом перетворення аргументів. Щоб функції могли повертати значення в програмуванні, ми використовуємо слово return, а потім вказуємо значення, яке має повернути виклик функції.

def return_number():
    return 42


result = return_number()
print(result)  # 42
def return_num(num):
    return num


res = return_num(10)
print(res)  # 10
def double(num):
    return num * 2


print(double(13))  # 26

Давайте визначимо згадані вище функції, які розглядалися на уроках математики. Почнімо з обчислення квадрата числа.

def square(x):
    return x * x


print(square(2))  # 4
print(square(4))  # 16
print(square(10))  # 100

З модулем (англ. absolute value) все не так просто. Модуль «позбавляється» знака перед числом. Наприклад, він залишає 10 без змін, але змінює -5 на 5. Відповідно до визначення, ця функція повинна повертати:

  • вхідне значення, якщо воно більше або дорівнює нулю;
  • протилежне вхідному значенню, коли воно менше нуля.

Щоб це зробити, ми можемо використовувати оператор if з блоком else.

def absolute(x):
    if x >= 0:
        return x
    else:
        return -x


print(absolute(0))  # 0
print(absolute(2))  # 2
print(absolute(-2))  # 2
print(absolute(10))  # 10
print(absolute(-10))  # 10

return негайно завершує виклик функції та повертає результат. Тому формально блок else не потрібний, оскільки, якщо перша умова використовує return, після цієї команди у функції вже нічого не виконуватиметься. Таким чином робота наведених вище функцій буде аналогічною до представлених нижче.

def square(x):
    return x * x
    print("Це ніколи не буде виведено ")


def absolute(x):
    if x >= 0:
        return x
    return -x

Нарешті, факторіал (англ. factorial). Факторіал n — це добуток чисел від 1 до n, тобто число, яке утворюється в результаті множення послідовних чисел. Наприклад, факторіал від 5 дорівнює 1 * 2 * 3 * 4 * 5, тобто 120. Факторіал чисел, менших за 1, вважається рівним 1. Ми можемо реалізувати це за допомогою циклу for. Щоб використовувати числа від 1 до n включно, ми використаємо range(1, num + 1). Визначимо змінну res, яка зберігатиме наш результат, і на кожному кроці циклу ми будемо множити цей результат на наступне число.

def factorial(num):
    result = 1
    for i in range(1, num + 1):
        result *= i
    return result

Це правильне рішення, але не єдине. Я хотів би показати зовсім інший спосіб мислення, який приведе нас до рішення, яке називається рекурентним. Погляньмо, як обчислюється факторіал для послідовних чисел (у математиці факторіал записується як ! після числа):

  • 1! = 1.
  • 2! = 1 * 2.
  • 3! = 1 * 2 * 3.
  • 4! = 1 * 2 * 3 * 4.
  • 5! = 1 * 2 * 3 * 4 * 5.
  • ...

Зверни увагу, що до послідовних чисел нашого рівняння просто додається ще одна дія множення.

  • 2! = 1! * 2.
  • 3! = 2! * 3.
  • 4! = 3! * 4.
  • 5! = 4! * 5.
  • ...

Отже, щоб знайти факторіал з 5, достатньо взяти факторіал з 4 і помножити результат на 5. Те ж стосується інших чисел, більших за 1. Щоб обчислити факторіал з n, потрібно обчислити факторіал з n — 1 і помножити його на n. Винятком є числа, менші за 1, для яких ми завжди повинні повертати значення 1. Цей алгоритм можна представити таким чином:

def factorial(num):
    if num <= 1:
        return 1

    return num * factorial(num — 1)


print(factorial(0))  # 1
print(factorial(1))  # 1
print(factorial(2))  # 2
print(factorial(3))  # 6
print(factorial(4))  # 24
print(factorial(5))  # 120

Щоб зрозуміти, як працює ця функція, розглянемо, як обчислюється значення factorial(3). Оскільки 3 більше ніж 1, ми можемо підставити 3 * factorial(2). Так само, оскільки 2 більше ніж 1, ми можемо його замінити: 3 * 2 * factorial(1). Оскільки 1 дорівнює 1, factorial(1) повертає 1, отже factorial(3) повертає 3 * 2 * 1, тобто 6.

Техніка виклику функції всередині себе називається рекурсією. Вона дуже важлива в програмуванні. Використовуючи рекурсію, завжди варто враховувати, чи не буде функція викликати саму себе нескінченно. У цьому випадку ми в безпеці, тому що за кожним разом значення num зменшується, і, коли функція доходить до 1, рекурсія завершується.

Вправа: Функції, які повертають значення

Напиши функцію, яка...

  • переводить дні у мілісекунди (одна секунда дорівнює 1000 мілісекунд);
  • обчислює площу прямокутного трикутника, виходячи з довжини його перпендикулярних сторін (формула a * b / 2);
  • повертає найбільше з трьох значень;
  • обчислює суму чисел у діапазоні, заданому як аргумент (без останнього числа, як у функції range).

Приклади використання нижче.

print(days_to_millis(1))  # 86400000
print(days_to_millis(3))  # 259200000

print(triangle_area(1, 1))  # 0.5
print(triangle_area(10, 20))  # 100

print(biggest(2, 3, 1))  # 3
print(biggest(2, 3, 5))  # 5
print(biggest(3, 3, 1))  # 3

print(sum_range(1, 4))  # 1 + 2 + 3, тобто 6
print(sum_range(2, 5))  # 2 + 3 + 4, тобто 9
print(sum_range(10, 12))  # 10 + 11, тобто 21

Відповіді в кінці книги.

Значення за замовчуванням, яке повертає функція

Якщо функція не повертає жодного значення при використанні return, вона за замовчуванням повертає None.

def fun():
    print("Just chillin’")


ret = fun() # Just chillin’
print(ret) # None

Аргументи за замовчуванням та іменовані аргументи

Чудовим функціоналом Python є можливість встановлювати значення для параметрів за замовчуванням. Наприклад, уяви, що Ти пишеш функцію, яка має запустити вебсторінку в браузері Chrome. Однак сторінку можна відкрити в звичайному або анонімному режимі. Ми не хочемо обирати режим кожного разу, коли викликається ця функція. Якщо користувач не зазначить іншого, використовуватиметься звичайний режим, а також завжди можна перейти до анонімного. В таких випадках у нагоді стає параметр зі значенням аргумента за замовчуванням.

Значення параметра за замовчуванням (тобто змінної у визначенні функції) вказується після знака рівності. Якщо параметр має таке значення, ми можемо викликати функцію без аргумента в цій позиції. У цьому випадку буде використано значення за замовчуванням.

def open_website(url, incognito=False):
    # Значення анонімного режиму за замовчуванням False
    if incognito:
        print(f"Opening {url} in incognito")
    else:
        print(f"Opening {url}")


open_website("a.com")  # Opening a.com
open_website("b.com", True)  
# Opening b.com in incognito

Уяви себе на місці людини, яка читає цей код і бачить open_website("b.com", True). Що означає True? Незрозуміло. Тут у нагоді стає ще один функціонал: іменування аргументів. Ми використовуємо його, додаючи перед аргументом (тобто у виклику функції) назву параметра і знак рівності.

open_website(url="c.com")  # Opening c.com
open_website("b.com", incognito=True)  
# Opening b.com in incognito
open_website(url="b.com", incognito=True)  
# Opening b.com in incognito

Коли аргументи вже названі, їх порядок не має значення.

open_website(incognito=True, url="b.com")  
# Opening b.com in incognito

Цей функціонал часто використовується на практиці. Зазвичай функції мають багато параметрів зі значеннями за замовчуванням, а при використанні визначається дуже мала їх кількість.

1:

"def" може бути скороченням від "define" або "definition", тобто "дефініція", "визначення".

2:

На початку і в кінці цієї назви стоїть по два підкреслення. Що стосується атрибутів, то про них ми поговоримо в наступному розділі.