Глава 15 Навіщо нам фунцкії

⏱️ Час на опанування теми: 15 хвилин

🤷 Для чого ми це вивчаємо:

🔑 Результати навчання:

  • Розуміння що таке програма, додаток та програмне забезпечення
  • Розуміння що таке алгоритм, кодування та програмування

🎈 Увага: Наразі ця глава знаходиться у стані активної розробки і ймовірно буде змінюватись і доповнюватись!


У цій главі…


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

15.1 Повторне використання

Фунцкію дозволяють використовувати один і той самий код скільки завгодно разів. Наприклад, у нас є список який складається з імен, які користувачі ввели в анкеті. Нажаль такі дані дуже часто є “брудними” і неоднорідними: вони можуть містити зайві пробіли, починатися з маленької літери, а не з великої тощо. Тому перед тим як аналізувати такі дані, нам треба їх почистити. У Додатку D ми глянули на два корисні методи стрічок, .trim() та .title, які прибирають пробіли на початку та у кінці стрічок та роблять першу букву кожного слова великою. Нам треба використати ці два методи на усі елементи списку за допомогою циклу for, щоб його відчистити:

names = ["  olha ", "hector", "  Volodymyr  "]
for i, element in enumerate(names):
    names[i] = names[i].strip() # видаляємо зайві пробіли
    names[i] = names[i].title() # робимо першу літеру великой
names
## ['Olha', 'Hector', 'Volodymyr']

Згодом, ми отримаємо ще і прізвища, і нам треба зробити те ж саме:

surnames = ["  martynovska  ", "Jimenez-Bravo", "  yaroslavskyi  "]
for i, element in enumerate(surnames):
    surnames[i] = surnames[i].strip() # видаляємо зайві пробіли
    surnames[i] = surnames[i].title() # робимо першу літеру великой
surnames
## ['Martynovska', 'Jimenez-Bravo', 'Yaroslavskyi']

Воу, це вже багато коду. А в нашій анкеті ще є міста, по-батькові тощо. Трюк у тому, що ми можемо загорнути цей цикл у фунцкію:

def clean_names(items): 
    for i, element in enumerate(items):
        items[i] = items[i].strip()
        items[i] = items[i].title()
    return items

І потім просто використати її приміняючи на кожному списку:

names = clean_names(names)
surnames = clean_names(surnames)
# ...

Це все добре, але в чому проблема копіювати постійно код? По-перше, коли ми копіюємо, дуже велика ймовірність зробити помилку у коді. Наприклад, забути поміняти names[i] на surnames[i] десь всередині циклу. По-друге, короткий та лаконічний код набагато простіше читати. І якщо вам потрібно буде повернутись і розібратись, що саме цей код робить, то вам буде простіше зрозуміти сто рядків замість тисячі.

Це твердження формулюється у концепт, який називається “Don’t repeat yourself”, або українською “Не повторюйся”. Якщо ви скопіювали та вставили якись код, дуже велика ймовірніть що краще “загорнути” цей код у функцію.

15.2 Модулярність

Так, це не дуже інтуітивне слово, але ми спробуємо пояснити його у простих термінах та привести зрозумілий приклад. Уявіть що вам треба потрапити з Харкова до Лозанни (невелике місто у Швйцарії). Скоріш за все ви будете використовувати громадський транспорт і купите білет на літак. Але по-перше, в аеропорт ще треба попасти, по-друге, в Лозані нема міжнародного аеропорту, тому вам треба буде прилетіти у Женеву, і звідти поїхати на потязі. Тобто ваша велика задача “потрапити з Харкова до Лозани” поділиться на маленькі під-задачки:

  • Проїхати із дому до Харківського аеропорту на метро
  • Перелитіти з Харкова до Києва
  • Пересісти на інший літак
  • Перелитіти з Києва в Женеву
  • Переїхати з Женеви до Лозани
  • Переїхати з Лозанського вокзалу до готелю

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

До нашого попереднього прикладу ми додамо ще вік, який теж був отриманий через анкету. Кожен елемент списку ages треба перевести з str до int:

ages = ["33", "44", "51"]

for i, element in enumerate(ages):
    ages[i] = int(ages[i])
    
ages
## [33, 44, 51]

А тепер ми цей код об’єднаємо з кодом який ми використовували для очищеня імен (до використання фунцкій):

names = ["  olha ", "hector", "  Volodymyr  "]
for i, element in enumerate(names):
    names[i] = names[i].strip()
    names[i] = names[i].title()

surnames = ["  martynovska  ", "Jimenez-Bravo", "  yaroslavskyi  "]
for i, element in enumerate(surnames):
    surnames[i] = surnames[i].strip()
    surnames[i] = surnames[i].title()

ages = ["33", "44", "51"]
for i, element in enumerate(ages):
    ages[i] = int(ages[i])

Трохи громіздко, чи не так? А ось якщо у нас буде дві фунцкії визначенні в окремомих клітинках Google Colab і одна де ми почистимо усі дані, це буде виглядати більш зрозуміло. Давайте визначемо фунцкії у наступній клітині:

# визначаємо фунцкії

def clean_names(items): 
    for i, element in enumerate(items):
        items[i] = items[i].strip()
        items[i] = items[i].title()
    return items
    
def convert_ages(items): 
    for i, element in enumerate(items):
        items[i] = int(items[i])
    return items

А тепер використаємо їх:

names = ["  olha ", "hector", "  Volodymyr  "]
names = clean_names(names)

surnames = ["  martynovska  ", "Jimenez-Bravo", "  yaroslavskyi  "]
surnames = clean_names(surnames)

ages = ["33", "44", "51"]
ages = convert_ages(ages)

15.3 Абстракція

Абстрація – теж не саме зрозуміле слово у світі. Ідея полягає у тому, щоб приховати усі деталі як саме фунцкія працює, залишаючи тільки необхідну інформацію. Ми про це вже говорили, коли казали що функція – це така собі чорна скриня. Попередній приклад – це також гарна ілюстрація ідеї абрстракії. Для того щоб зрозуміти що саме коїться у попередньому коді, у нас нема необхідності розуміти деталі реалізації convert_ages(). Все що нам треба знати – параметри функції, її призначення та у якому форматі вона повертає значення.

Наприклад, якщо код фунцкії convert_ages() буде виглядати наступним чином:

def convert_ages(items): 
    return list(map(int, items))

Це ні як не вплине на розуміння того, як працюють наступні рядки, тому що ми можемо сприймати convert_ages() як чорну скриню:

ages = ["33", "44", "51"]
ages = convert_ages(ages)


🤸 Вправи
1. Що означає принцип Don’t repeat yourself (Не повторюйся)?
2. Які переваги модулярності в програмуванні?
3. Що означає принцип абстракції в програмуванні?
4. Що означає чорна скриня в контексті абстракції?
5. Який принцип програмування допомагає уникнути реплікації коду та зробити його легше читабельним?
6. Що можна зробити, використовуючи абстракцію в програмуванні?