У цій інструкції ви ознайомитеся з використанням функції timeit
з модуля timeit
в Python. Ви навчитеся вимірювати час виконання окремих виразів і функцій у вашому коді Python.
Аналіз часу виконання коду може допомогти вам оцінити продуктивність різних частин програми та виявити області, що потребують оптимізації.
Спочатку ми розглянемо структуру функції timeit
. Потім застосуємо її на практиці, щоб зрозуміти, як вимірювати час виконання блоків коду та функцій у ваших Python-проєктах. Почнемо!
Використання функції timeit
в Python
Модуль timeit
є частиною стандартної бібліотеки Python. Щоб його використовувати, потрібно імпортувати:
import timeit
Синтаксис функції timeit
з модуля timeit
виглядає наступним чином:
timeit.timeit(stmt, setup, number)
Де:
stmt
– це фрагмент коду, час виконання якого потрібно виміряти. Це може бути як простий рядок Python, так і багаторядковий, або ж ім’я функції.setup
– це код, який виконується один раз перед вимірюванням часу виконанняstmt
. Часто використовується для підготовки даних або імпорту необхідних модулів. Наприклад, при вимірюванні часу створення масиву NumPy, імпортnumpy
буде кодомsetup
.number
– це кількість разів, яку потрібно виконатиstmt
. Значення за замовчуванням – 1 мільйон (1 000 000), але ви можете встановити будь-яке інше значення.
Тепер, коли ми розібрали синтаксис timeit()
, перейдемо до прикладів коду.
Вимірювання часу простих виразів Python
У цьому розділі ми застосуємо timeit
для вимірювання часу виконання простих виразів Python.
Запустіть інтерактивний інтерпретатор Python (REPL) і випробуйте наведені нижче приклади. Ми виміряємо час операцій піднесення до степеня та цілочисельного ділення для 10 000 і 100 000 ітерацій.
Зверніть увагу, що вираз, час виконання якого ми вимірюємо, передається як рядок Python, а різні вирази в рядку розділені крапкою з комою.
>>> import timeit
>>> timeit.timeit('3**4;3//4',number=10000)
0.0004020999999738706
>>> timeit.timeit('3**4;3//4',number=100000)
0.0013780000000451764
Запуск timeit
з командного рядка
Функцію timeit
можна використовувати і з командного рядка. Ось еквівалентний виклик timeit
з командного рядка:
$ python -m timeit -n [number] -s [setup] [stmt]
python -m timeit
означає, що ми запускаємоtimeit
як головний модуль.-n
– це параметр командного рядка, який визначає кількість разів виконання коду. Відповідає аргументуnumber
у функціїtimeit()
.- Параметр
-s
використовується для визначення кодуsetup
.
Перепишемо попередній приклад, використовуючи командний рядок:
$ python -m timeit -n 100000 '3**4;3//4'
100000 loops, best of 5: 35.8 nsec per loop
У цьому прикладі ми вимірюємо час виконання вбудованої функції len()
. Ініціалізація рядка – це код setup
, який передається через параметр -s
.
$ python -m timeit -n 100000 -s "string_1 = 'coding'" 'len(string_1)'
100000 loops, best of 5: 239 nsec per loop
Зверніть увагу, що вихідні дані показують час виконання найкращого з 5 запусків. Коли ви запускаєте timeit
з командного рядка, параметр повторення (-r
) за замовчуванням дорівнює 5. Це означає, що виконання stmt
зазначену кількість разів повторюється 5 разів, і виводиться найкращий час виконання.
Аналіз методів розвороту рядків за допомогою timeit
При роботі з рядками Python іноді потрібно їх розвернути. Існує два основних підходи для розвороту рядка:
- Використання зрізів рядків
- Використання функції
reversed()
та методуjoin()
.
Розворот рядків Python за допомогою зрізів
Поглянемо, як працюють зрізи рядків і як їх можна використати для розвороту рядка Python. Синтаксис some_string[start:stop]
повертає частину рядка від індексу start
до stop-1
. Розглянемо приклад.
Нехай є рядок “Python”. Він має довжину 6, а індекси символів – від 0 до 5.
>>> string_1 = 'Python'
Вказуючи початкове та кінцеве значення, ми отримуємо частину рядка. Наприклад, string_1[1:4]
поверне 'yth'
.
>>> string_1 = 'Python'
>>> string_1[1:4]
'yth'
Якщо початкове значення не вказано, то за замовчуванням використовується 0, і зріз починається з нульового індексу.
Тут кінцеве значення – 3, тому зріз починається з індексу 0 і закінчується на індексі 2.
>>> string_1[:3]
'Pyt'
Якщо ви не вкажете індекс зупинки, зріз почнеться зі вказаного початкового індексу (1) і продовжиться до кінця рядка.
>>> string_1[1:]
'ython'
Ігноруючи як початкове, так і кінцеве значення, ми отримаємо копію всього рядка.
>>> string_1[::]
'Python'
Спробуємо створити зріз із кроком. Встановимо початкове, кінцеве та крокові значення рівними 1, 5 і 2 відповідно. Ми отримаємо частину рядка, починаючи з індексу 1 до індексу 4 (за винятком кінцевої точки 5), що містить кожен другий символ.
>>> string_1[1:5:2]
'yh'
Використовуючи від’ємний крок, можна отримати зріз, починаючи з кінця рядка. З кроком -2, string_1[5:2:-2]
дасть наступний зріз:
>>> string_1[5:2:-2]
'nh'
Отже, щоб отримати розгорнуту копію рядка, ми пропускаємо початкове та кінцеве значення і встановлюємо крок у -1, як показано нижче:
>>> string_1[::-1]
'nohtyP'
Коротко: рядок[::-1]
повертає перевернуту копію рядка.
Розворот рядків за допомогою вбудованих функцій та методів рядків
Вбудована функція reversed()
у Python повертає зворотний ітератор над елементами рядка.
>>> string_1 = 'Python'
>>> reversed(string_1)
<reversed object at 0x00BEAF70>
Ви можете перебрати зворотний ітератор за допомогою циклу for
:
for char in reversed(string_1):
print(char)
І отримати доступ до елементів рядка в зворотному порядку.
# Вивід
n
o
h
t
y
P
Потім можна викликати метод join()
для зворотного ітератора: <sep>.join(reversed(some-string))
.
Наведений нижче фрагмент коду показує кілька прикладів, де роздільником є дефіс і пробіл відповідно.
>>> '-'.join(reversed(string1))
'n-o-h-t-y-P'
>>> ' '.join(reversed(string1))
'n o h t y P'
Тут нам не потрібен роздільник; тому встановимо роздільник у порожній рядок, щоб отримати перевернуту копію рядка:
>>> ''.join(reversed(string1))
'nohtyP'
Використання .join(reversed(some-string))
повертає перевернуту копію рядка.
Порівняння часу виконання за допомогою timeit
Отже, ми розглянули два способи розвороту рядків Python. Який з них швидший? Давайте з’ясуємо.
У попередньому прикладі з вимірюванням часу простих виразів Python ми не використовували код setup
. Зараз ми розгортаємо рядок Python. Операція розвороту виконується певну кількість разів (number
), а код setup
(ініціалізація рядка) виконується лише один раз.
>>> import timeit
>>> timeit.timeit(stmt="string_1[::-1]", setup = "string_1 = 'Python'", number = 100000)
0.04951830000001678
>>> timeit.timeit(stmt = "''.join(reversed(string_1))", setup = "string_1 = 'Python'", number = 100000)
0.12858760000000302
Для однакової кількості ітерацій (number
), підхід із зрізом рядка виявився швидшим, ніж використання методу join()
та функції reversed()
.
Вимірювання часу виконання функцій Python за допомогою timeit
У цьому розділі ми вивчимо, як використовувати функцію timeit
для вимірювання часу виконання функцій Python. Маючи список рядків, наступна функція hasDigit
повертає список рядків, що містять хоча б одну цифру.
def hasDigit(somelist):
str_with_digit = []
for string in somelist:
check_char = [char.isdigit() for char in string]
if any(check_char):
str_with_digit.append(string)
return str_with_digit
Зараз ми хочемо виміряти час виконання функції hasDigit()
за допомогою timeit
.
Спочатку визначимо вираз (stmt
), час виконання якого ми вимірюватимемо. Це буде виклик функції hasDigit()
зі списком рядків як аргумент. Далі визначимо код setup
. Здогадуєтесь, що має містити код setup
?
Для успішного виклику функції код setup
має містити:
- Визначення функції
hasDigit()
- Ініціалізацію списку рядків (аргументу).
Визначимо код setup
у рядку налаштувань, як показано нижче:
setup = """
def hasDigit(somelist):
str_with_digit = []
for string in somelist:
check_char = [char.isdigit() for char in string]
if any(check_char):
str_with_digit.append(string)
return str_with_digit
thislist=['puffin3','7frost','blue']
"""
Тепер ми можемо використовувати функцію timeit
і отримати час виконання функції hasDigit()
для 100 000 запусків.
import timeit
timeit.timeit('hasDigit(thislist)',setup=setup,number=100000)
# Вивід
0.2810094920000097
Висновок
Ви навчилися використовувати функцію timeit
в Python для вимірювання часу виразів, функцій та інших викликів. Це дозволить вам порівнювати продуктивність коду, час виконання різних реалізацій однієї і тієї ж функції тощо.
Підсумуємо, чому ви навчилися у цьому посібнику. Ви можете використовувати функцію timeit()
зі синтаксисом timeit.timeit(stmt=…,setup=…,number=…)
. Крім того, можна запускати timeit
з командного рядка для вимірювання часу коротких фрагментів коду.
Як наступний крок, ви можете вивчити інші інструменти профілювання Python, наприклад, line-profiler
і memprofiler
, для профілювання коду щодо часу та пам’яті відповідно.
Далі ви дізнаєтеся, як обчислити різницю в часі в Python.