V tomto návodu se podíváme na to, jak pracovat s funkcí timeit
z modulu timeit
v Pythonu. Ukážeme si, jak měřit dobu běhu jednoduchých výrazů a funkcí.
Měření rychlosti kódu vám pomůže zjistit, jak dlouho trvá provedení určité části programu a také odhalit místa, která je vhodné optimalizovat.
Začneme tím, že si vysvětlíme syntaxi funkce timeit
. Následně si ukážeme praktické příklady, jak ji využít k měření doby provádění bloků kódu a funkcí ve vašem Python projektu. Pusťme se do toho.
Jak používat funkci timeit
v Pythonu
Modul timeit
je součástí standardní knihovny Pythonu, takže ho stačí pouze importovat:
import timeit
Následuje syntaxe pro použití funkce timeit
z modulu timeit
:
timeit.timeit(stmt, setup, number)
Kde:
stmt
představuje blok kódu, jehož dobu provádění chceme změřit. Může to být jednoduchý Python řetězec, víceřádkový řetězec, nebo název volatelného objektu.setup
, jak název napovídá, je blok kódu, který se provede pouze jednou, typicky jako příprava před spuštěnímstmt
. Například pokud měříte dobu vytváření NumPy pole, tak import knihovny NumPy budesetup
kód a samotné vytvoření pole budestmt
.number
je parametr, který určuje, kolikrát se mástmt
provést. Výchozí hodnota je 1 milion (1 000 000), ale můžete nastavit libovolnou jinou hodnotu.
Nyní, když známe syntaxi funkce timeit()
, můžeme se podívat na praktické příklady.
Měření doby běhu jednoduchých výrazů v Pythonu
V této sekci se budeme zabývat měřením doby provádění jednoduchých Python výrazů pomocí funkce timeit
.
Spusťte Python REPL a vyzkoušejte si následující příklady. Zde měříme dobu provádění umocňování a celočíselného dělení pro 10 000 a 100 000 opakování.
Je důležité si všimnout, že kód pro měření předáváme jako řetězec a k oddělení jednotlivých výrazů používáme středník.
>>> import timeit
>>> timeit.timeit('3**4;3//4',number=10000)
0.0004020999999738706
>>> timeit.timeit('3**4;3//4',number=100000)
0.0013780000000451764
Spuštění timeit
z příkazové řádky
Funkci timeit
můžeme použít i z příkazové řádky. Ekvivalentní volání z příkazové řádky je následující:
$ python -m timeit -n [number] -s [setup] [stmt]
python -m timeit
indikuje, že spouštímetimeit
jako hlavní modul.-n
je parametr příkazové řádky, který specifikuje, kolikrát se má kód spustit. Je to ekvivalent argumentunumber
ve funkcitimeit()
.- S parametrem
-s
definujeme kód pro nastavení prostředí.
Zde je přepsaný předchozí příklad pomocí ekvivalentu z příkazové řádky:
$ python -m timeit -n 100000 '3**4;3//4'
100000 loops, best of 5: 35.8 nsec per loop
V tomto příkladu měříme dobu provádění vestavěné funkce len()
. Inicializace řetězce je kód setup
, který předáváme pomocí parametru -s
.
$ python -m timeit -n 100000 -s "string_1 = 'coding'" 'len(string_1)'
100000 loops, best of 5: 239 nsec per loop
Ve výstupu si můžeme všimnout, že se nám zobrazuje doba běhu nejlepšího z 5 opakování. Co to znamená? Když spustíte timeit
z příkazové řádky, parametr -r
(počet opakování) je implicitně nastaven na 5. To znamená, že se kód stmt
pro zadaný počet opakování spustí pětkrát a vrátí se nejlepší čas provedení.
Analýza metod pro obrácení řetězce pomocí timeit
Při práci s řetězci v Pythonu je často potřeba obrátit jejich pořadí. Dva nejběžnější přístupy k obrácení řetězců jsou:
- Použití řezů (slicing) řetězce.
- Použití funkce
reversed()
a metodyjoin()
.
Obrácení řetězce pomocí řezů
Pojďme se podívat, jak funguje krájení řetězců a jak ho můžete použít pro obrácení řetězce v Pythonu. Syntaxí some_string[start:stop]
získáme část řetězce začínající na indexu start
a končící na indexu stop-1
. Zkusme si příklad.
Mějme řetězec ‚Python‘. Tento řetězec má délku 6 a seznam indexů je 0, 1, 2 až 5.
>>> string_1 = 'Python'
Když zadáme počáteční i koncovou hodnotu, získáme řez, který sahá od počátku až do konce-1. Proto string_1[1:4]
vrátí ‚yth‘.
>>> string_1 = 'Python'
>>> string_1[1:4]
'yth'
Pokud nezadáme počáteční hodnotu, použije se výchozí hodnota 0 a řez začíná od indexu nula až do indexu stop – 1.
Zde je koncová hodnota 3, takže řez začíná na indexu 0 a jde až k indexu 2.
>>> string_1[:3]
'Pyt'
Pokud nezahrneme koncový index, řez začne od počátečního indexu (1) a sahá až na konec řetězce.
>>> string_1[1:]
'ython'
Pokud vynecháme počáteční i koncovou hodnotu, získáme kopii celého řetězce.
>>> string_1[::]
'Python'
Vytvořme řez s hodnotou kroku. Nastavíme start, stop a krok na 1, 5 a 2. Získáme část řetězce začínající na 1 až do 4 (kromě koncového bodu 5), obsahující každý druhý znak.
>>> string_1[1:5:2]
'yh'
Když použijeme záporný krok, můžeme získat řez začínající od konce řetězce. S krokem nastaveným na -2, string_1[5:2:-2]
nám dá následující výsledek:
>>> string_1[5:2:-2]
'nh'
Pro získání obrácené kopie řetězce vynecháme počáteční a koncové hodnoty a nastavíme krok na -1, jak je ukázáno níže:
>>> string_1[::-1]
'nohtyP'
Ve zkratce: string[::-1]
vrací obrácenou kopii řetězce.
Obrácení řetězců s vestavěnými funkcemi a metodami řetězců
Vestavěná funkce reversed()
v Pythonu vrací iterátor, který prochází prvky řetězce v opačném pořadí.
>>> string_1 = 'Python'
>>> reversed(string_1)
<reversed object at 0x00BEAF70>
Můžeme tedy iterovat pomocí smyčky for
přes tento iterátor a získat tak přístup k jednotlivým znakům v opačném pořadí:
for char in reversed(string_1):
print(char)
# Výstup:
n
o
h
t
y
P
Dále můžeme volat metodu join()
na tomto iterátoru se syntaxí:
.
Následující úryvek kódu ukazuje několik příkladů, kde je oddělovačem spojovník a mezera.
>>> '-'.join(reversed(string1))
'n-o-h-t-y-P'
>>> ' '.join(reversed(string1))
'n o h t y P'
Pokud nechceme oddělovač, nastavíme ho na prázdný řetězec, abychom získali obrácenou kopii řetězce:
>>> ''.join(reversed(string1))
'nohtyP'
Použitím .join(reversed(some_string))
získáme obrácenou kopii řetězce.
Porovnání časů provádění pomocí timeit
Zatím jsme se naučili dva způsoby, jak obrátit pořadí znaků v řetězci. Ale který z nich je rychlejší? Zkusme to zjistit.
V předchozím příkladu, kde jsme měřili dobu běhu jednoduchých výrazů v Pythonu, jsme nepoužili žádný setup
kód. Zde obrácení řetězce provádíme. Zatímco operace obrácení řetězce se spustí tolikrát, kolikrát udává parametr number
, setup
kód, což je inicializace řetězce, se provede pouze jednou.
>>> 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
Pro stejný počet opakování je metoda řezů rychlejší než použití join()
a reversed()
.
Měření doby běhu Python funkcí pomocí timeit
V této části se podíváme na to, jak měřit dobu provádění Python funkcí pomocí funkce timeit
. Mějme funkci hasDigit()
, která pro daný seznam řetězců vrátí seznam těch, které obsahují alespoň jednu číslici.
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
Nyní bychom rádi změřili dobu provádění této funkce pomocí timeit
.
Nejprve identifikujeme kód, který chceme měřit (stmt
). V tomto případě je to volání funkce hasDigit()
s argumentem seznamu řetězců. Dále definujeme setup
kód. Tušíte, jak by měl vypadat?
Aby bylo volání funkce úspěšné, měl by setup
kód obsahovat následující:
- Definice funkce
hasDigit()
. - Inicializace seznamu řetězců.
Definujeme setup
kód v řetězci, jak je ukázáno níže:
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']
"""
Nyní můžeme použít funkci timeit
a získat dobu provádění funkce hasDigit()
pro 100 000 spuštění.
import timeit
timeit.timeit('hasDigit(thislist)',setup=setup,number=100000)
# Výstup:
0.2810094920000097
Závěr
Naučili jste se, jak používat funkci timeit
v Pythonu pro měření doby běhu výrazů, funkcí a dalších objektů. To vám může pomoci porovnávat váš kód, analyzovat dobu běhu různých implementací stejné funkcionality, a další.
Pojďme si shrnout, co jsme se naučili v tomto návodu. Funkci timeit()
můžete volat se syntaxí timeit.timeit(stmt=…,setup=…,number=…)
. Alternativně můžete timeit
spustit z příkazové řádky pro měření malých úryvků kódu.
Jako další krok si můžete prozkoumat další Python balíčky pro profilování, jako jsou line-profiler
a memprofiler
, pro profilování kódu z hlediska času i paměti.
Dále se můžete naučit, jak vypočítat časový rozdíl v Pythonu.