Створення функцій
Останнє оновлення 2026-06-19 | Редагувати цю сторінку
Огляд
Питання
- Як створювати власні функції?
Цілі
- Знайдіть і поясніть різницю між визначенням функції та викликом функції.
- Створити функцію, яка приймає невелику фіксовану кількість вхідних аргументів і повертає єдиний результат.
Щоб програми було легше зрозуміти, розбивайте їх на функції.
- Людина може одночасно зберігати лише декілька речей у своїй робочій пам’яті.
- Розуміння складніших/більших ідей досягається шляхом осмислення та
поєднання їхніх складових.
- Компоненти в машині.
- Леми при доведенні теорем.
- Функції служать тій же меті в програмах.
- Інкапсулюють (тобто приховують) складність, щоб ми могли розглядати її як єдине “ціле”.
- Також сприяють повторному використанню кода.
- Пишемо один раз, використовуємо багаторазово.
Функція визначається за допомогою def та задається її
назвою, параметрами та блоком коду.
- Визначення нової функції починається з
def. - Далі йде назва функції.
- Назви функцій мають відповідати тим самим правилам, що й імена змінних.
- Потім параметри в дужках.
- Порожні дужки, якщо функція не приймає жодних вхідних даних.
- Ми обговоримо це детально нижче.
- Потім двокрапка.
- Потім блок коду з відступом.
Визначення функції не виконує її.
- Визначення функції не виконує її.
- Аналогічно до присвоєння значення змінній.
- Щоб виконати код функції, її необхідно викликати.
ВИВІД
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
Ми також можемо назвати аргументи під час виклику, що дозволяє передавати їх у довільному порядку та підвищує читабельність виклику. В іншому випадку під час читання коду може виникнути непорозуміння, наприклад, який аргумент йде другим: місяць або день.
ВИВІД
1871/3/19
- Корисна аналогія (https://twitter.com/minisciencegirl/status/693486088963272705):
()містить інгредієнти для функції, тоді як тіло містить рецепт.
Функції можуть повертати результат свого виклику за допомогою
return.
- Використовуйте
return ...для повернення результату виклику функції. - Може виникнути будь-де у функції.
- Але функції легше зрозуміти, якщо
returnзустрічається:- На початку функції для обробки особливих випадків.
- У самому кінці з остаточним результатом.
ВИВІД
average of actual values: 2.6666666666666665
ВИВІД
average of empty list: None
- Пам’ятайте: кожна функція щось повертає.
- Функція, яка не містить
returnявно, автоматично повертаєNone.
ВИВІД
1871/3/19
result of call is: None
Виявлення синтаксичних помилок
- Прочитайте наведений нижче код і спробуйте знайти помилки без його запуску.
- Запустіть код і прочитайте повідомлення про помилку. Це
SyntaxErrorчиIndentationError? - Виправте помилку.
- Повторюйте кроки 2 та 3 доки не виправите всі помилки.
ВИВІД
calling <function report at 0x7fd128ff1bf8> 22.5
Виклик функції завжди потребує круглі дужки, інакше повертається
адреса об’єкта функції в пам’яті. Отже, якщо ми хочемо викликати функцію
з назвою report і надати їй значення 22,5 для обробки,
виклик функції матиме такий вигляд:
ВИВІД
calling
pressure is 22.5
Порядок виконання операцій
- Що не так у цьому прикладі?
PYTHON
result = print_time(11, 37, 59)
def print_time(hour, minute, second):
time_string = str(hour) + ':' + str(minute) + ':' + str(second)
print(time_string)
- Після виправлення проблеми вище поясніть, чому виконання цього прикладу:
дає такий результат:
ВИВІД
11:37:59
result of call is: None
- Чому результатом виклику є
None?
Проблема цього прикладу полягає в тому, що функція
print_time()визначається після виклику функції. Python не може розпізнати ім’яprint_timeоскільки воно ще не визначено і генерує помилкуNameError, тобтоNameError: name 'print_time' is not definedПерший рядок виводу
11:37:59з’являється завдяки першому рядку кодуresult = print_time(11, 37, 59). Він викликає функціюprint_timeі присвоює повернуте нею значення зміннійresult. Другий рядок є результатом наступного виклику функціїprint, який виводить вміст змінноїresult.print_time()явно не повертає значення за допомогоюreturn, тому автоматично повертаєNone.
Пошук першого від’ємного значення
Заповніть порожні поля, щоб створити функцію, яка приймає список чисел як аргумент і повертає перше від’ємне значення в списку. Що робить ваша функція, якщо список порожній? Що відбувається, якщо список не містить жодного від’ємного числа?
Виклик з іменованими аргументами
Раніше ми розглядали цю функцію:
PYTHON
def print_date(year, month, day):
joined = str(year) + '/' + str(month) + '/' + str(day)
print(joined)
Ми бачили, що можна викликати функцію за допомогою *іменованих аргументів *, наприклад:
- Що друкує
print_date(day=1, month=2, year=2003)? - Коли ви раніше бачили подібний виклик функції?
- За яких умов і з якою метою доцільно використовувати іменовані аргументи при виклику функцій?
2003/2/1- Ми бачили приклади використання іменованих аргументів під час роботи
з бібліотекою pandas. Наприклад, під час читання набору даних за
допомогою
data = pd.read_csv('data/gapminder_gdp_europe.csv', index_col='country'), останній аргументindex_colє іменованим аргументом. - Використання іменованих аргументів покращує читабельність коду — з виклику функції можна побачити, які імена мають аргументи всередині функції. Це також зменшує ймовірність передачі аргументів у неправильному порядку, оскільки при використанні іменованих аргументів порядок не має значення.
Винесення блоку 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))
- Створіть функцію
print_egg_label(), яка працюватиме з новою версією програми, наведеною вище. Зверніть увагу на значення, яке повертає функціяget_egg_label(). Зразок виводу програми вище буде71.23 large. - Брудне яйце може мати масу понад 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']
- Заповніть наведені нижче пропуски. Результатом має бути середній ВВП Японії за роками, які присутні у наборі даних та належать до 1980-х.
PYTHON
year = 1983
gdp_decade = 'gdpPercap_' + str(year // ____)
avg = (japan.loc[gdp_decade + ___] + japan.loc[gdp_decade + ___]) / 2
- Перетворіть наведений вище код в окрему функцію.
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 та 9 для кожного десятиліття? (Підказка: використовуйте стовпці, щоб відфільтрувати їх за десятиліттям.)
- Середній ВВП Японії по відзвітованих роках з 1980-тих обчислюється за допомогою:
PYTHON
year = 1983
gdp_decade = 'gdpPercap_' + str(year // 10)
avg = (japan.loc[gdp_decade + '2'] + japan.loc[gdp_decade + '7']) / 2
- Цей код як функція:
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
- Щоб отримати середнє значення за відповідні роки, нам потрібно застосувати цикл:
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
Ця функція тепер може викликатися наступним чином:
ВИВІД
20880.023800000003
Моделювання динамічної системи
У математиці динамічна система - це система, у якій функція описує залежність розташування точки в геометричному просторі від часу. Канонічний приклад динамічної системи - це логістичне відображення, тобто модель зростання, яка обчислює нову щільність популяції (від 0 до 1) на основі її поточного значення. В цій моделі час приймає дискретні значення 0, 1, 2, … (тобто змінюється кроками, а не плавно)
- Визначте функцію під назвою
logistic_map, яка приймає два аргументи:x, що представляє популяцію в момент часуt, та параметрr=1. Ця функція має повертати значення, що представляє стан системи (популяції) у момент часуt + 1, використовуючи наступну функцію:
f(t+1) = r * f(t) * [1 - f(t)]
Використовуючи цикл
forабоwhile, повторюйте виклик функціїlogistic_map, визначеної в частині 1. Початкове значення густини популяції становить 0.5, а часовий інтервал —t_final = 10. Зберігайте проміжні результати в списку, щоб після завершення циклу ви накопичили послідовність значень, що представляють стан системи в моменти часуt = [0,1,...,t_final](11 значень в цілому). Виведіть цей список, щоб побачити, як змінюється популяція з часом.Помістить цей цикл у функцію під назвою
iterate, яка отримує три вхідні параметри: початкове значення густини популяції,t_finalтаr. Функція має повертати список значень, що представляють стан системи в моменти часуt = [0,1,...,t_final]. Виконайте цю функцію для періодівt_final = 100та1000, і виведіть деякі з отриманих значень. Чи рухається популяція до стабільного стану?
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.