Ten poradnik wprowadzi Cię w tajniki wykorzystania funkcji timeit
z modułu timeit
języka Python. Zdobędziesz wiedzę na temat mierzenia czasu wykonania zarówno prostych wyrażeń, jak i złożonych funkcji w Pythonie.
Pomiar czasu wykonywania fragmentów kodu jest niezwykle przydatny. Pozwala oszacować czas potrzebny na wykonanie danego fragmentu, a także zlokalizować te części kodu, które wymagają optymalizacji.
Rozpocznijmy od omówienia składni funkcji timeit
. Następnie przejdziemy do praktycznych przykładów, aby zademonstrować, jak można jej użyć do pomiaru czasu wykonania bloków kodu i funkcji w Pythonie. Zatem, do dzieła!
Wykorzystanie funkcji timeit w Pythonie
Moduł timeit
jest integralną częścią standardowej biblioteki Pythona, a jego import jest bardzo prosty:
import timeit
Poniżej prezentowana jest składnia funkcji timeit
, którą znajdziemy w module o tej samej nazwie:
timeit.timeit(stmt, setup, number)
Gdzie:
stmt
oznacza fragment kodu, którego czas wykonania chcemy zmierzyć. Może to być prosty ciąg znaków w Pythonie, ciąg wieloliniowy lub nazwa funkcji do wywołania.setup
, jak sama nazwa wskazuje, to kod, który musi zostać wykonany tylko raz, zazwyczaj jako przygotowanie do uruchomieniastmt
. Na przykład, jeśli mierzymy czas tworzenia tablicy NumPy, import biblioteki NumPy będzie kodem setup, a samo tworzenie tablicy będzie instrukcją (stmt
).- Parametr
number
określa, ile razystmt
ma zostać uruchomiony. Domyślną wartością jest milion (1000000), ale można ustawić dowolną inną liczbę.
Teraz, kiedy już znamy składnię timeit()
, przejdźmy do praktycznych przykładów.
Pomiar czasu prostych wyrażeń w Pythonie
W tej sekcji spróbujemy zmierzyć czas wykonania prostych wyrażeń w Pythonie za pomocą timeit
.
Uruchom interaktywną konsolę Pythona (REPL) i przetestuj poniższe przykłady. Zmierzymy czas wykonania operacji potęgowania i dzielenia całkowitego dla 10000 i 100000 powtórzeń.
Zauważ, że instrukcję do pomiaru czasu przekazujemy jako ciąg znaków Pythona, używając średnika do rozdzielenia poszczególnych wyrażeń.
>>> import timeit >>> timeit.timeit('3**4;3//4',number=10000) 0.0004020999999738706 >>> timeit.timeit('3**4;3//4',number=100000) 0.0013780000000451764
Użycie timeit z wiersza poleceń
Funkcję timeit
można również wywoływać bezpośrednio z wiersza poleceń. Poniżej znajduje się ekwiwalent wywołania funkcji timeit
z poziomu terminala:
$ python -m timeit -n [number] -s [setup] [stmt]
python -m timeit
oznacza, że uruchamiamytimeit
jako główny moduł.- Opcja
-n
w wierszu poleceń określa, ile razy kod powinien zostać wykonany. Jest to odpowiednik argumentunumber
w funkcjitimeit()
. - Można użyć opcji
-s
do zdefiniowania kodu przygotowawczego (setup).
Poniżej przedstawiamy poprzedni przykład, używając ekwiwalentu z wiersza poleceń:
$ python -m timeit -n 100000 '3**4;3//4' 100000 loops, best of 5: 35.8 nsec per loop
W tym przykładzie mierzymy czas wykonania wbudowanej funkcji len()
. Inicjalizacja ciągu znaków jest kodem instalacyjnym przekazanym za pomocą opcji -s
.
$ python -m timeit -n 100000 -s "string_1 = 'coding'" 'len(string_1)' 100000 loops, best of 5: 239 nsec per loop
W wynikach otrzymujemy czas wykonania dla najlepszego z 5 powtórzeń. Co to oznacza? Podczas uruchamiania timeit
z wiersza poleceń opcja powtarzania -r
ma domyślną wartość 5. Oznacza to, że wykonanie instrukcji (stmt
) określoną liczbę razy jest powtarzane pięć razy, a zwracany jest najlepszy z uzyskanych czasów.
Analiza metod odwracania ciągów znaków za pomocą timeit
Podczas pracy z ciągami znaków w Pythonie, często zachodzi potrzeba ich odwrócenia. Najczęściej stosowane metody odwracania ciągów znaków to:
- Wykorzystanie mechanizmu wycinania (slicing).
- Użycie funkcji
reversed()
i metodyjoin()
.
Odwracanie ciągów znaków za pomocą mechanizmu wycinania (slicing)
Przyjrzyjmy się, jak działa mechanizm wycinania i jak można go użyć do odwrócenia ciągu znaków. Składnia jakiś-ciąg[start:stop]
zwraca fragment ciągu, zaczynając od indeksu start
do indeksu stop-1
. Rozważmy poniższy przykład.
Weźmy pod uwagę ciąg znaków „Python”. Ma on długość 6, a indeksy poszczególnych znaków to 0, 1, 2, 3, 4 i 5.
>>> string_1 = 'Python'
Jeśli określimy zarówno wartość startową, jak i końcową, otrzymamy fragment ciągu rozciągający się od start
do stop-1
. Na przykład, string_1[1:4]
zwróci 'yth’.
>>> string_1 = 'Python' >>> string_1[1:4] 'yth'
Jeśli wartość startowa nie zostanie określona, domyślnie przyjmowana jest wartość zero, a wycinek zaczyna się od indeksu zero i rozciąga się do indeksu stop-1
.
W tym przypadku wartość stop
wynosi 3, więc wycinek zaczyna się od indeksu 0 i kończy na indeksie 2.
>>> string_1[:3] 'Pyt'
Jeśli nie podamy indeksu stop
, wycinek zacznie się od indeksu startowego (1) i rozciągnie się do końca ciągu.
>>> string_1[1:] 'ython'
Pominięcie zarówno wartości startowej, jak i końcowej zwróci wycinek zawierający cały ciąg znaków.
>>> string_1[::] 'Python'
Możemy również tworzyć wycinki z określoną wartością kroku. Ustawmy wartości start
, stop
i krok
odpowiednio na 1, 5 i 2. Uzyskamy wycinek ciągu znaków zaczynający się od indeksu 1 i rozciągający się do indeksu 4 (bez włączenia punktu końcowego 5), który zawiera co drugi znak.
>>> string_1[1:5:2] 'yh'
Używając ujemnej wartości kroku, możemy uzyskać wycinek zaczynający się od końca ciągu znaków. Przy kroku ustawionym na -2, string_1[5:2:-2]
da nam następujący wycinek:
>>> string_1[5:2:-2] 'nh'
Aby uzyskać odwróconą kopię ciągu znaków, pomijamy wartości startową i końcową i ustawiamy krok na -1, jak pokazano poniżej:
>>> string_1[::-1] 'nohtyP'
Podsumowując, ciąg[::-1]
zwraca odwróconą kopię ciągu.
Odwracanie ciągów znaków za pomocą wbudowanych funkcji i metod
Wbudowana funkcja reversed()
w Pythonie zwraca odwrotny iterator po elementach ciągu.
>>> string_1 = 'Python' >>> reversed(string_1) <reversed object at 0x00BEAF70>
Możemy przejść przez ten odwrotny iterator za pomocą pętli for:
for char in reversed(string_1): print(char)
I uzyskać dostęp do znaków ciągu w odwrotnej kolejności.
# Output n o h t y P
Następnie możemy wywołać metodę join()
na odwrotnym iteratorze, stosując składnię:
.
Poniższy kod przedstawia kilka przykładów, gdzie separatorem jest myślnik i spacja.
>>> '-'.join(reversed(string1)) 'n-o-h-t-y-P' >>> ' '.join(reversed(string1)) 'n o h t y P'
W tym przypadku nie chcemy żadnego separatora, ustawiamy separator na pusty ciąg, aby otrzymać odwróconą kopię ciągu:
>>> ''.join(reversed(string1)) 'nohtyP'
Zatem .join(reversed(some-string))
zwraca odwróconą kopię ciągu znaków.
Porównanie czasów wykonania za pomocą timeit
Do tej pory poznaliśmy dwa sposoby na odwrócenie ciągu znaków w Pythonie. Który z nich jest szybszy? Sprawdźmy to!
W poprzednim przykładzie, gdy mierzyliśmy czas prostych wyrażeń, nie mieliśmy kodu przygotowawczego. Teraz, gdy odwracamy ciąg znaków, operacja odwrócenia będzie wykonywana określoną liczbę razy, a kod przygotowawczy będzie inicjalizacją ciągu, która zostanie wykonana tylko raz.
>>> 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
Przy tej samej liczbie powtórzeń, podejście z użyciem wycinania ciągu jest szybsze niż metoda wykorzystująca join()
i reversed()
.
Pomiar czasu wykonania funkcji w Pythonie za pomocą timeit
W tej sekcji nauczymy się, jak mierzyć czas wykonania funkcji w Pythonie za pomocą timeit
. Rozważmy funkcję hasDigit
, która przyjmuje listę ciągów i zwraca listę tych, które zawierają co najmniej jedną cyfrę.
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
Chcemy teraz zmierzyć czas wykonania tej funkcji za pomocą timeit
.
Najpierw musimy zidentyfikować instrukcję (stmt
), którą chcemy zmierzyć. Będzie to wywołanie funkcji hasDigit()
z listą ciągów jako argumentem. Następnie musimy zdefiniować kod przygotowawczy (setup
). Jak myślisz, jaki powinien być kod setup
?
Aby wywołanie funkcji przebiegło poprawnie, kod przygotowawczy powinien zawierać:
- Definicję funkcji
hasDigit()
. - Inicjalizację listy ciągów znaków, która będzie argumentem funkcji.
Zdefiniujmy kod przygotowawczy w łańcuchu setup
, jak pokazano poniżej:
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'] """
Możemy teraz wykorzystać funkcję timeit
i uzyskać czas wykonania funkcji hasDigit()
dla 100000 powtórzeń.
import timeit timeit.timeit('hasDigit(thislist)',setup=setup,number=100000)
# Output 0.2810094920000097
Podsumowanie
Nauczyłeś się, jak używać funkcji timeit
w Pythonie do pomiaru czasu wykonania wyrażeń, funkcji i innych obiektów wywoływalnych. Ta wiedza pozwoli Ci przeprowadzać testy porównawcze kodu, analizować różne implementacje tej samej funkcji, i wiele więcej.
Podsumowując to, czego się nauczyliśmy, możemy użyć funkcji timeit()
stosując składnię timeit.timeit(stmt=…,setup=…,number=…)
. Alternatywnie, możemy uruchomić timeit
z wiersza poleceń, aby zmierzyć czas wykonania krótkich fragmentów kodu.
Następnym krokiem może być zapoznanie się z innymi pakietami do profilowania kodu w Pythonie, takimi jak line-profiler
i memprofiler
, które umożliwiają profilowanie kodu pod względem czasu i zużycia pamięci.
Kolejnym krokiem może być nauka obliczania różnic czasu w Pythonie.