Поширені проблеми/нюанси роботи з функціями¶
Список/словник, у який збираються дані у функції, створений за межами функції¶
Дуже часто у завданнях зустрічається такий нюанс: функція має зібрати якісь дані до списку/словника та список створений поза функцією. Тоді здається що функція працює правильно, але при цьому тест не проходить. Це відбувається тому, що в такому варіанті кожен виклик функції додає елементи до того ж списку:
result = []
def func(items):
for i in items:
result.append(i*100)
return result
In [3]: func([1, 2, 3])
Out[3]: [100, 200, 300]
In [4]: func([7, 8])
Out[4]: [100, 200, 300, 700, 800]
Виправити це можна перенесенням рядка створення списку в функцію:
def func(items):
result = []
for i in items:
result.append(i*100)
return result
In [21]: func([1, 2, 3])
Out[21]: [100, 200, 300]
In [22]: func([7, 8])
Out[22]: [700, 800]
Все, що стосується функції краще завжди писати всередині функції. Тест тут не проходить тому що всередині файлу завдання функція викликається вперше - все правильно, а потім тест викликає її вдруге і там раптом вдвічі більше даних, ніж потрібно.
Значення за замовчуванням у параметрах створюються під час створення функції¶
Приклад функції, яка повинна виводити поточну дату та час кожного виклику:
from datetime import datetime
import time
def print_current_datetime(ptime=datetime.now()):
print(f">>> {ptime}")
for i in range(3):
print("Імітуємо довге виконання...")
time.sleep(1)
print_current_datetime()
Імітуємо довге виконання...
>>> 2021-02-23 09:01:49.845425
Імітуємо довге виконання...
>>> 2021-02-23 09:01:49.845425
Імітуємо довге виконання...
>>> 2021-02-23 09:01:49.845425
Через те що datetime.now()
вказано у значенні за замовчуванням, це значення створюється
під час створення функції. І після, коли функція викликається, час один і той же.
Для коректної роботи треба викликати datetime.now()
в тілі функції:
Другий приклад де цей нюанс може призвести до несподіваних результатів, якщо про нього не знати - змінні типи даних у значенні за замовчуванням.
Наприклад, використання списку у значенні за замовчуванням:
У цьому випадку список data створюється один раз - при створенні функції, а під час виклику функції, дані додаються в один і той же список. У результаті всі повторні виклики будуть додавати елементи:
In [16]: add_item(1)
Out[16]: [1]
In [17]: add_item(2)
Out[17]: [1, 2]
In [18]: add_item(4)
Out[18]: [1, 2, 4]
Якщо потрібно зробити так, щоб параметр data був необов'язковим і за замовчуванням створювався порожній список, треба зробити так:
def add_item(item, data=None):
if data is None:
data = []
data.append(item)
return data
In [23]: add_item(1)
Out[23]: [1]
In [24]: add_item(2)
Out[24]: [2]
Виняток UnboundLocalError: local variable referenced before assignment¶
Помилка може виникнути у таких випадках:
- звернення до змінної функції йде до її створення - це може бути випадковість (помилка) або наслідок того, що якась умова не виконалася
- звернення всередині функції до глобальної змінної, але при цьому всередині функції створена така ж змінна пізніше
Перший випадок – звернулися до змінної до її створення:
def f():
print(b)
b = 55
In [6]: f()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
Input In [6], in <cell line: 1>()
----> 1 f()
Input In [5], in f()
1 def f():
----> 2 print(b)
3 b = 55
UnboundLocalError: local variable 'b' referenced before assignment
Змінна створена в умові, а умова не виконана:
def f():
if 5 > 8:
b = 55
print(b)
In [8]: f()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
Input In [8], in <cell line: 1>()
----> 1 f()
Input In [7], in f()
2 if 5 > 8:
3 b = 55
----> 4 print(b)
UnboundLocalError: local variable 'b' referenced before assignment
Ім'я глобальної та локальної змінної однакове і всередині функції спочатку йде спроба звернення до глобальної, потім створення локальної:
a = 10
def f():
print(a)
a = 55
print(a)
In [4]: f()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 f()
Input In [3], in f()
1 def f():
----> 2 print(a)
3 a = 55
4 print(a)
UnboundLocalError: local variable 'a' referenced before assignment
Python повинен визначити область видимості змінної. І у випадку функцій Python
вважає, що змінна локальна, якщо вона створена всередині функції. На момент
коли ми доходимо до виклику функції, Python вже вирішив, що a
це локальна змінна
і саме через це перший рядок print(a)
дає помилку.