Створення функцій

Останнє оновлення 2026-06-19 | Редагувати цю сторінку

Приблизний час: 25 хвилин

Огляд

Питання

  • Як створювати власні функції?

Цілі

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

Щоб програми було легше зрозуміти, розбивайте їх на функції.


  • Людина може одночасно зберігати лише декілька речей у своїй робочій пам’яті.
  • Розуміння складніших/більших ідей досягається шляхом осмислення та поєднання їхніх складових.
    • Компоненти в машині.
    • Леми при доведенні теорем.
  • Функції служать тій же меті в програмах.
    • Інкапсулюють (тобто приховують) складність, щоб ми могли розглядати її як єдине “ціле”.
  • Також сприяють повторному використанню кода.
    • Пишемо один раз, використовуємо багаторазово.

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


  • Визначення нової функції починається з def.
  • Далі йде назва функції.
    • Назви функцій мають відповідати тим самим правилам, що й імена змінних.
  • Потім параметри в дужках.
    • Порожні дужки, якщо функція не приймає жодних вхідних даних.
    • Ми обговоримо це детально нижче.
  • Потім двокрапка.
  • Потім блок коду з відступом.

PYTHON

def print_greeting():
    print('Hello!')
    print('The weather is nice today.')
    print('Right?')

Визначення функції не виконує її.


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

PYTHON

print_greeting()

ВИВІД

Hello!

Аргументи виклику функції зіставляються з параметрами, з якими вона була визначена.


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

PYTHON

def print_date(year, month, day):
    joined = str(year) + '/' + str(month) + '/' + str(day)
    print(joined)

print_date(1871, 3, 19)

ВИВІД

1871/3/19

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

PYTHON

print_date(month=3, day=19, year=1871)

ВИВІД

1871/3/19

Функції можуть повертати результат свого виклику за допомогою return.


  • Використовуйте return ... для повернення результату виклику функції.
  • Може виникнути будь-де у функції.
  • Але функції легше зрозуміти, якщо return зустрічається:
    • На початку функції для обробки особливих випадків.
    • У самому кінці з остаточним результатом.

PYTHON

def average(values):
    if len(values) == 0:
        return None
    return sum(values) / len(values)

PYTHON

a = average([1, 3, 4])
print('average of actual values:', a)

ВИВІД

average of actual values: 2.6666666666666665

PYTHON

print('average of empty list:', average([]))

ВИВІД

average of empty list: None

PYTHON

result = print_date(1871, 3, 19)
print('result of call is:', result)

ВИВІД

1871/3/19
result of call is: None
Вправа

Виявлення синтаксичних помилок

  1. Прочитайте наведений нижче код і спробуйте знайти помилки без його запуску.
  2. Запустіть код і прочитайте повідомлення про помилку. Це SyntaxError чи IndentationError?
  3. Виправте помилку.
  4. Повторюйте кроки 2 та 3 доки не виправите всі помилки.

PYTHON

def another_function
  print("Syntax errors are annoying.")
   print("But at least python tells us about them!")
  print("So they are usually not too hard to fix.")

PYTHON

def another_function():
  print("Syntax errors are annoying.")
  print("But at least Python tells us about them!")
  print("So they are usually not too hard to fix.")
Вправа

Визначення та використання

Що друкує наступна програма?

PYTHON

def report(pressure):
    print('pressure is', pressure)

print('calling', report, 22.5)

ВИВІД

calling <function report at 0x7fd128ff1bf8> 22.5

Виклик функції завжди потребує круглі дужки, інакше повертається адреса об’єкта функції в пам’яті. Отже, якщо ми хочемо викликати функцію з назвою report і надати їй значення 22,5 для обробки, виклик функції матиме такий вигляд:

PYTHON

print("calling")
report(22.5)

ВИВІД

calling
pressure is 22.5
Вправа

Порядок виконання операцій

  1. Що не так у цьому прикладі?

PYTHON

result = print_time(11, 37, 59)

def print_time(hour, minute, second):
   time_string = str(hour) + ':' + str(minute) + ':' + str(second)
   print(time_string)
  1. Після виправлення проблеми вище поясніть, чому виконання цього прикладу:

PYTHON

result = print_time(11, 37, 59)
print('result of call is:', result)

дає такий результат:

ВИВІД

11:37:59
result of call is: None
  1. Чому результатом виклику є None?
  1. Проблема цього прикладу полягає в тому, що функція print_time() визначається після виклику функції. Python не може розпізнати ім’я print_time оскільки воно ще не визначено і генерує помилку NameError, тобто NameError: name 'print_time' is not defined

  2. Перший рядок виводу 11:37:59 з’являється завдяки першому рядку коду result = print_time(11, 37, 59). Він викликає функцію print_time і присвоює повернуте нею значення змінній result. Другий рядок є результатом наступного виклику функції print, який виводить вміст змінної result.

  3. print_time() явно не повертає значення за допомогою return, тому автоматично повертає None.

Вправа

Згортання коду у функцію

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

PYTHON

import pandas as pd

def min_in_data(____):
    data = ____
    return ____

PYTHON

import pandas as pd

def min_in_data(filename):
    data = pd.read_csv(filename)
    return data.min()
Вправа

Пошук першого від’ємного значення

Заповніть порожні поля, щоб створити функцію, яка приймає список чисел як аргумент і повертає перше від’ємне значення в списку. Що робить ваша функція, якщо список порожній? Що відбувається, якщо список не містить жодного від’ємного числа?

PYTHON

def first_negative(values):
    for v in ____:
        if ____:
            return ____

PYTHON

def first_negative(values):
    for v in values:
        if v < 0:
            return v

Якщо до функції передати порожній список або список з лише додатними значеннями, вона повертає None:

PYTHON

my_list = []
print(first_negative(my_list))

ВИВІД

None
Вправа

Виклик з іменованими аргументами

Раніше ми розглядали цю функцію:

PYTHON

def print_date(year, month, day):
    joined = str(year) + '/' + str(month) + '/' + str(day)
    print(joined)

Ми бачили, що можна викликати функцію за допомогою *іменованих аргументів *, наприклад:

PYTHON

print_date(day=1, month=2, year=2003)
  1. Що друкує print_date(day=1, month=2, year=2003)?
  2. Коли ви раніше бачили подібний виклик функції?
  3. За яких умов і з якою метою доцільно використовувати іменовані аргументи при виклику функцій?
  1. 2003/2/1
  2. Ми бачили приклади використання іменованих аргументів під час роботи з бібліотекою pandas. Наприклад, під час читання набору даних за допомогою data = pd.read_csv('data/gapminder_gdp_europe.csv', index_col='country'), останній аргумент index_col є іменованим аргументом.
  3. Використання іменованих аргументів покращує читабельність коду — з виклику функції можна побачити, які імена мають аргументи всередині функції. Це також зменшує ймовірність передачі аргументів у неправильному порядку, оскільки при використанні іменованих аргументів порядок не має значення.
Вправа

Винесення блоку If/Print в окрему функцію

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

PYTHON

import random
for i in range(10):

    # імітація маси курячого яйця
    # маса має випадкове значення у диапазоні 70 +/- 20 грамів
    mass = 70 + 20.0 * (2.0 * random.random() - 1.0)

    print(mass)

    # друк етикетки машиною для сортування яєць
    if mass >= 85:
        print("jumbo")
    elif mass >= 70:
        print("large")
    elif mass < 70 and mass >= 55:
        print("medium")
    else:
        print("small")

Блок if, який класифікує яйця, може бути корисним в інших ситуаціях. Щоб уникнути його повторення доцільно виділити його в окрему функцію get_egg_label(). Переписавши програму з використанням зазначеної функції, отримаємо такий результат:

PYTHON

# revised version
import random
for i in range(10):

    # імітація маси курячого яйця
    # маса має випадкове значення у диапазоні 70 +/- 20 грамів
    mass = 70 + 20.0 * (2.0 * random.random() - 1.0)

    print(mass, get_egg_label(mass))
  1. Створіть функцію print_egg_label(), яка працюватиме з новою версією програми, наведеною вище. Зверніть увагу на значення, яке повертає функція get_egg_label(). Зразок виводу програми вище буде 71.23 large.
  2. Брудне яйце може мати масу понад 90 грамів, а зіпсоване чи розбите яйце, ймовірно, матиме масу менше ніж 50 грамів. Змініть функцію print_egg_label() для врахування цих умов. Можливий вивід програми: 25 too light, probably spoiled.

PYTHON

def get_egg_label(mass):
    # друк етикетки машиною для сортування яєць
    egg_label = "Unlabelled"
    if mass >= 90:
        egg_label = "warning: egg might be dirty"
    elif mass >= 85:
        egg_label = "jumbo"
    elif mass >= 70:
        egg_label = "large"
    elif mass < 70 and mass >= 55:
        egg_label = "medium"
    elif mass < 50:
        egg_label = "too light, probably spoiled"
    else:
        egg_label = "small"
    return egg_label
Вправа

Функція для аналізу даних

Припустимо, що був виконаний наступний код:

PYTHON

import pandas as pd

data_asia = pd.read_csv('data/gapminder_gdp_asia.csv', index_col=0)
japan = data_asia.loc['Japan']
  1. Заповніть наведені нижче пропуски. Результатом має бути середній ВВП Японії за роками, які присутні у наборі даних та належать до 1980-х.

PYTHON

year = 1983
gdp_decade = 'gdpPercap_' + str(year // ____)
avg = (japan.loc[gdp_decade + ___] + japan.loc[gdp_decade + ___]) / 2
  1. Перетворіть наведений вище код в окрему функцію.

PYTHON

def avg_gdp_in_decade(country, continent, year):
    data_countries = pd.read_csv('data/gapminder_gdp_'+___+'.csv',delimiter=',',index_col=0)
    ____
    ____
    ____
    return avg
  1. Як узагальнити функцію, якщо роки у стовпцях заздалегідь невідомі? Наприклад, якщо набір даних також містить роки, що закінчуються на 1 та 9 для кожного десятиліття? (Підказка: використовуйте стовпці, щоб відфільтрувати їх за десятиліттям.)
  1. Середній ВВП Японії по відзвітованих роках з 1980-тих обчислюється за допомогою:

PYTHON

year = 1983
gdp_decade = 'gdpPercap_' + str(year // 10)
avg = (japan.loc[gdp_decade + '2'] + japan.loc[gdp_decade + '7']) / 2
  1. Цей код як функція:

PYTHON

def avg_gdp_in_decade(country, continent, year):
    data_countries = pd.read_csv('data/gapminder_gdp_' + continent + '.csv', index_col=0)
    c = data_countries.loc[country]
    gdp_decade = 'gdpPercap_' + str(year // 10)
    avg = (c.loc[gdp_decade + '2'] + c.loc[gdp_decade + '7'])/2
    return avg
  1. Щоб отримати середнє значення за відповідні роки, нам потрібно застосувати цикл:

PYTHON

def avg_gdp_in_decade(country, continent, year):
    data_countries = pd.read_csv('data/gapminder_gdp_' + continent + '.csv', index_col=0)
    c = data_countries.loc[country]
    gdp_decade = 'gdpPercap_' + str(year // 10)
    total = 0.0
    num_years = 0
    for yr_header in c.index: # індекс c містить звітні роки
        if yr_header.startswith(gdp_decade):
            total = total + c.loc[yr_header]
            num_years = num_years + 1
    return total/num_years

Ця функція тепер може викликатися наступним чином:

PYTHON

avg_gdp_in_decade('Japan','asia',1983)

ВИВІД

20880.023800000003
Вправа

Моделювання динамічної системи

У математиці динамічна система - це система, у якій функція описує залежність розташування точки в геометричному просторі від часу. Канонічний приклад динамічної системи - це логістичне відображення, тобто модель зростання, яка обчислює нову щільність популяції (від 0 до 1) на основі її поточного значення. В цій моделі час приймає дискретні значення 0, 1, 2, … (тобто змінюється кроками, а не плавно)

  1. Визначте функцію під назвою logistic_map, яка приймає два аргументи: x, що представляє популяцію в момент часу t, та параметр r=1. Ця функція має повертати значення, що представляє стан системи (популяції) у момент часу t + 1, використовуючи наступну функцію:

f(t+1) = r * f(t) * [1 - f(t)]

  1. Використовуючи цикл for або while, повторюйте виклик функції logistic_map, визначеної в частині 1. Початкове значення густини популяції становить 0.5, а часовий інтервал — t_final = 10. Зберігайте проміжні результати в списку, щоб після завершення циклу ви накопичили послідовність значень, що представляють стан системи в моменти часу t = [0,1,...,t_final] (11 значень в цілому). Виведіть цей список, щоб побачити, як змінюється популяція з часом.

  2. Помістить цей цикл у функцію під назвою iterate, яка отримує три вхідні параметри: початкове значення густини популяції, t_final та r. Функція має повертати список значень, що представляють стан системи в моменти часу t = [0,1,...,t_final]. Виконайте цю функцію для періодів t_final = 100 та 1000, і виведіть деякі з отриманих значень. Чи рухається популяція до стабільного стану?

PYTHON

def logistic_map(x, r):
    return r * x * (1 - x)

PYTHON

initial_population = 0.5
t_final = 10
r = 1.0
population = [initial_population]

for t in range(t_final):
    population.append( logistic_map(population[t], r) )

PYTHON

def iterate(initial_population, t_final, r):
    population = [initial_population]
    for t in range(t_final):
        population.append( logistic_map(population[t], r) )
    return population

for period in (10, 100, 1000):
    population = iterate(0.5, period, 1)
    print(population[-1])

ВИВІД

0.06945089389714401
0.009395779870614648
0.0009913908614406382

Здається, популяція наближається до нуля.

Примітка

Використання функцій з умовними операторами в Pandas

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

PYTHON

def calculate_life_quartile(exp):
    if exp < 58.41:
        # Це спостереження знаходиться в першому квартилі
        return 1
    elif exp >= 58.41 and exp < 67.05:
        # Це спостереження знаходиться у другому квартилі
       return 2
    elif exp >= 67.05 and exp < 71.70:
        # Це спостереження знаходиться у третьому квартилі
       return 3
    elif exp >= 71.70:
        # Це спостереження знаходиться в четвертому квартилі
       return 4
    else:
        # Це спостереження містить невірні дані
       return None

calculate_life_quartile(62.5)

ВИВІД

2

Зазвичай така функція використовується всередині циклу for. Але Pandas має інший, більш ефективний спосіб робити те саме - застосування функції до датафрейму або його частини. Розглянемо приклад, використовуючи наведене вище визначення функції.

PYTHON

data = pd.read_csv('data/gapminder_all.csv')
data['life_qrtl'] = data['lifeExp_1952'].apply(calculate_life_quartile)

У другому рядку коду багато цікавого, тож розберімо його по частинах. Праворуч від = ми починаємо з data['lifeExp'] — це стовпець датафрейму data, що має мітку lifExp. Ми використовуємо apply(), щоб застосувати функцію calculate_life_quartile до усіх значень цього стовпця.

Ключові моменти
  • Розбивайте програми на функції, щоб їх було легше зрозуміти.
  • Функції визначаються за допомогою def з назвою, параметрами та блоком коду.
  • Визначення функції не запускає її.
  • Аргументи виклику функції зіставляються з її визначеними параметрами.
  • Функції можуть повертати результат свого виклику за допомогою return.