Написання функцій
Огляд
Викладання: 10 хв
Вправи: 15 хвПитання
Як я можу створити власні функції?
Цілі
Пояснити і визначитие різницю між визначенням функції та викликом функції.
Написати функцію, яка приймає невелику фіксовану кількість аргументів і видає єдиний результат.
Розбийте програми на функції, щоб їх було легше зрозуміти.
- Людина може одночасно зберігати лише кілька елементів у робочій пам’яті.
- Розуміти великі/складніші ідеї, розуміючи та комбінуючи їх частини.
- Компоненти в машині.
- Леми при доведенні теорем.
- Функції служать тій же меті в програмах.
- Інкапсулю.nm складність, щоб ми могли розглядати їх як одну «річ».
- Також уможливлюють повторне використання.
- Пишемо один раз, використовуємо багато разів..
Визначте функцію за допомогою def
з назвою, параметрами та блоком коду.”
- Почніть визначення нової функції з
def
. - Далі йде назва функції.
- Назви функцій мають відповідати тим самим правилам, що й імена змінних.
- Потім параметри в дужках.
- Порожні дужки, якщо функція не приймає жодних вхідних даних.
- Ми наразі обговоримо це детально.
- Потім двокрапка.
- Потім блок коду з відступом.
def print_greeting():
print('Hello!')
Визначення функції не запускає її.
- Визначення функції не запускає її.
- Як присвоєння значення змінній.
- Необхідно викликати функцію, щоб виконати код, який вона містить.
print_greeting()
Hello!
Аргументи у виклику зіставляються з параметрами у визначенні.
- Функції найбільш корисні, коли вони можуть працювати з різними даними.
- Укажіть параметри під час визначення функції.
- Вони стають змінними під час виконання функції.
- Праметрам присвоюються аргументи виклику (тобто значення, передані у функцію)
- Якщо ви не називаєте аргументи під час їх використання у виклику, аргументи будуть зіставлені з параметрами в тому порядку, у якому вони визначені у функції.
def print_date(year, month, day):
joined = str(year) + '/' + str(month) + '/' + str(day)
print(joined)
print_date(1871, 3, 19)
1871/3/19
Або ми можемо назвати аргументи під час виклику функції, що дозволяє нам це зробити вказати їх у довільному порядку:
print_date(month=3, day=19, year=1871)
1871/3/19
- Згідно Twitter:
()
містить інгредієнти для функції тоді як тіло містить рецепт.
Функції можуть повертати результат до свого виклику за допомогою return
.
- Використовуте
return ...
щоб повернути значення у місце виклику. - Може виникнути будь-де у функції.
- Але функції легше зрозуміти, якщо реалізовано функцію
return
:- На початку функції для обробки особливих випадків.
- У самому кінці з остаточним результатом.
def average(values):
if len(values) == 0:
return None
return sum(values) / len(values)
a = average([1, 3, 4])
print('середнє фактичних значень:', a)
середнє фактичних значень: 2.6666666666666665
print('середнє порожнього списку:', average([]))
середнє порожнього списку: None
- Пам’ятайте: кожна функція щось повертає.
- Функція, яка явно не містить
return
, автоматично повертаєNone
.
result = print_date(1871, 3, 19)
print('результат виклику є таким:', result)
1871/3/19
результат виклику є таким: None
Виявлення синтаксичних помилок
- Прочитайте наведений нижче код і спробуйте визначити, у чому полягають помилки без запуску.
- Запустіть код і прочитайте повідомлення про помилку. Це
SyntaxError
чиIndentationError
?- Виправте помилку.
- Повторюйте кроки 2 та 3 доки не виправите всі помилки.
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.")
Рішення
def another_function(): print("Синтаксичні помилки дратують.") print("Але принаймні Python розповідає нам про них!") print("Тож їх зазвичай не надто важко виправити.")
Визначення та використання
Що друкує наступна програма?
def report(pressure): print('тиск', pressure) print('виклик', report, 22.5)
Рішення
calling <function report at 0x7fd128ff1bf8> 22.5
Для виклику функції завжди потрібні круглі дужки, інакше ви отримаєте адресу пам’яті об’єкта функції. Отже, якщо ми хочемо викликати функцію під назвою report і надати їй значення 22,5 для звіту, ми могли б викликати нашу функцію так
print("виклик") report(22.5)
Порядок виконання операцій
Приклад вище:
result = print_date(1871, 3, 19) print('результат виклику:', result)
надруковано:
1871/3/19 результат виклику: None
Поясніть, чому два рядки виводу з’явилися в такому порядку.
Що не так у цьому прикладі?
result = print_date(1871,3,19) def print_date(year, month, day): joined = str(year) + '/' + str(month) + '/' + str(day) print(joined)
Рішення
- Перший рядок виводу (
1871/3/19
) є результатом функції друку всерединіprint_date()
, тоді як другий рядок з функції друку під викликом функції. Весь код всерединіprint_date()
виконується спочатку, а потім програма “залишає” функцію та виконує решту коду.- Проблема з прикладом полягає в тому, що функція визначається після виклику функції. Тому Python не розуміє виклик функції.
Інкапсуляція
Заповніть порожні поля, щоб створити функцію, яка приймає одне ім’я файлу як аргумент, завантажує дані у файл, названий аргументом, і повертає мінімальне значення цих даних.
import pandas as pd def min_in_data(____): data = ____ return ____
Рішення
import pandas as pd def min_in_data(filename): data = pd.read_csv(filename) return data.min()
Знайди Перший
Заповніть порожні поля, щоб створити функцію, яка приймає список чисел як аргумент і повертає перше від’ємне значення в списку. Що робить ваша функція, якщо список порожній?
def first_negative(values): for v in ____: if ____: return ____
Рішення
def first_negative(values): for v in values: if v<0: return v
Якщо цій функції передається порожній список, вона повертає
None
:my_list = [] print(first_negative(my_list))
None
Виклик по імені
Раніше ми розглядали таку функцію:
def print_date(year, month, day): joined = str(year) + '/' + str(month) + '/' + str(day) print(joined)
Ми побачили, що можна викликати функцію за допомогою іменованих аргументів, наприклад:
print_date(day=1, month=2, year=2003)
- Що друкує
print_date(day=1, month=2, year=2003)
?- Коли ви раніше бачили подібний виклик функції?
- Коли і чому корисно викликати функції таким чином?
Рішення
2003/2/1
- Ми бачили приклади використання іменованих аргументів під час роботи з бібліотекою pandas. Наприклад, під час читання в наборі даних using
data = pd.read_csv('data/gapminder_gdp_europe.csv', index_col='country')
, останній аргументindex_col
є іменованим аргументом.- Використання іменованих аргументів може зробити код більш читабельним, оскільки з виклику функції можна побачити, які імена мають різні аргументи всередині функції. Це також може зменшити ймовірність передачі аргументів у неправильному порядку, оскільки за допомогою іменованих аргументів порядок не має значення.
Інкапсуляція блоку If/Print
Наведений нижче код запускатиметься на принтері етикеток для курячих яєць. Цифрові ваги повідомлять комп’ютеру про масу курячого яйця (у грамах), а потім комп’ютер друкує етикетку.
Будь ласка, перепишіть код так, щоб if-блок був вкладений у функцію.
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("джамбо") elif(mass>=70): print("велике") elif(mass<70 and mass>=55): print("середнє") else: print("мале")
Далі спрощена програма. Яке визначення функції зробить його функціональним?
# Адаптована версія import random for i in range(10): # імітація маси курячого яйця # (випадкова) маса становитиме 70 +/- 20 грамів mass=70+20.0*(2.0*random.random()-1.0) print(mass,print_egg_label(mass))
- Створіть визначення функції для
print_egg_label()
, яка працюватиме з адаптованою програмою вище. Зверніть увагу, що чисельне значення, яке повертає функція, буде важливим. Зразок оформлення результату може мати вигляд71.23 large
.- Брудне яйце може мати масу понад 90 грамів, а зіпсоване чи розбите яйце, ймовірно, матиме масу менше 50 грамів. Змініть свою функцію
print_egg_label()
для врахування цих умов помилки. Вихідний зразок може бути25 занадто легке, можливо, зіпсоване
.Рішення
def print_egg_label(mass): #egg sizing machinery prints a label if(mass>=90): return("попередження: яйце може бути брудним") elif(mass>=85): return("джамбо") elif(mass>=70): return("велике") elif(mass<70 and mass>=55): return("середнє") elif(mass<50): return("занадто легке, можливо, зіпсоване") else: return("мале")
Інкапсуляція аналізу даних
Припустімо, що наступний код було виконано:
import pandas as pd df = pd.read_csv('data/gapminder_gdp_asia.csv', index_col=0) japan = df.loc['Japan']
1.Заповніть наведені нижче твердження, щоб отримати середній ВВП Японії за роками, зазначеними у 1980-х роках.
year = 1983 gdp_decade = 'gdpPercap_' + str(year // ____) avg = (japan.loc[gdp_decade + ___] + japan.loc[gdp_decade + ___]) / 2
2.Абстрагуйте код вище в одну функцію.
def avg_gdp_in_decade(country, continent, year): df = pd.read_csv('data/gapminder_gdp_'+___+'.csv',delimiter=',',index_col=0) ____ ____ ____ return avg
3.Як би ви узагальнили цю функцію якщо ви не знали заздалегідь, які конкретні роки зустрічаються як стовпці в даних? Наприклад, що, якби ми також мали дані за роки, що закінчуються на 1 і 9 для кожного десятиліття? (Підказка: використовуйте стовпці, щоб відфільтрувати ті, що відповідають десятиліттям, замість того, щоб перелічувати їх у коді.)
Рішення
1.
year = 1983 gdp_decade = 'gdpPercap_' + str(year // 10) avg = (japan.loc[gdp_decade + '2'] + japan.loc[gdp_decade + '7']) / 2
2.
def avg_gdp_in_decade(country, continent, year): df = pd.read_csv('data/gapminder_gdp_' + continent + '.csv', index_col=0) c = df.loc[country] gdp_decade = 'gdpPercap_' + str(year // 10) avg = (c.loc[gdp_decade + '2'] + c.loc[gdp_decade + '7'])/2 return avg
3.
Нам потрібно переглянути звітні роки для отримання середнього значення для відповідних у даних.
def avg_gdp_in_decade(country, continent, year): df = pd.read_csv('data/gapminder_gdp_' + continent + '.csv', index_col=0) c = df.loc[country] gdp_decade = 'gdpPercap_' + str(year // 10) total = 0.0 num_years = 0 for yr_header in c.index: # c's index contains reported years if yr_header.startswith(gdp_decade): total = total + c.loc[yr_header] num_years = num_years + 1 return total/num_years
Функцію тепер можна викликати:
avg_gdp_in_decade('Japan','asia',1983)
20880.023800000003
Моделювання динамічної системи
У математиці динамічна система це система, у якій функція описує залежність точки в геометричному просторі від часу. Канонічним прикладом динамічної системи є система під назвою [логістичне відображення] (https://en.wikipedia.org/wiki/Logistic_map).
Визначте функцію під назвою
logistic_map
, яка приймає два входи:x
, що представляє стан системи в момент часу t, і параметрr
. Ця функція має повертати значення, що представляє стан системи в момент часу t+1.Використовуючи цикл
for
, повторіть функціюlogistic_map
, визначену в частині 1, починаючи з початкової умови 0,5 для періодівt_final=10
,100
, і1000
. Зберігайте проміжні результати в списку, щоб після завершення циклуfor
ви накопичили послідовність значень, що представляють станlogistic_map
в моменти часу t=0,1,…,t_final.Інкапсулюйте логіку вашого циклу
for
у функцію під назвоюiterate
, яка приймає початкову умову як перший вхід, параметрt_final
як другий вхід і параметрr
як третій вхід. Функція має повертати список значень, що представляють стан логістичної карти в момент часу t=0,1,…,t_final.Рішення
1.
def logistic_map(x, r): return r * x * (1 - x)
2.
initial_condition = 0.5 t_final = 10 r = 1.0 trajectory = [initial_condition] for t in range(1, t_final): trajectory.append( logistic_map(trajectory[t-1], r) )
3.
def iterate(initial_condition, t_final, r): trajectory = [initial_condition] for t in range(1, t_final): trajectory.append( logistic_map(trajectory[t-1], r) ) return trajectory
Ключові моменти
Розбийте програми на функції, щоб їх було легше зрозуміти.
Визначте функцію за допомогою
def
з назвою, параметрами та блоком коду.Визначення функції не запускає її.
Аргументи у виклику зіставляються з параметрами у визначенні.
Функції можуть повертати результат своєго виклику за допомогою
return
.