Язык программирования Python
Шрифт:
5 for i in s:
6 -> lst.append(ord(i))
7 return lst
[EOF]
(Pdb) p lst
[]
(Pdb) p vars
{'i': 'A', 's': 'ABCDE', 'lst': []}
(Pdb) n
> /examples/myfun.py(5)fun
— > for i in s:
(Pdb) p vars
{'i': 'A', 's': 'ABCDE', 'lst': [65]}
(Pdb) n
> /examples/myfun.py(6)fun
— > lst.append(ord(i))
(Pdb) n
> /examples/myfun.py(5)fun
— > for i in s:
(Pdb) p vars
{'i': 'B', 's': 'ABCDE', 'lst': [65, 66]}
(Pdb) r
— Return - > /examples/myfun.py(7)fun — >[65, 66, 67, 68, 69]
— > return lst
(Pdb) n
[65, 66, 67, 68, 69]
>>>
Интерактивный
Разумеется, некоторые интерактивные оболочки разработчика для Python предоставляют функции отладчика. Кроме того, отладку достаточно легко организовать, поставив в ключевых местах программы, операторы print для вывода интересующих параметров. Обычно этого достаточно, чтобы локализовать проблему. В CGI–сценариях можно использовать модуль cgitb, о котором говорилось в одной из предыдущих лекций.
Профайлер
Для определения мест в программе, на выполнение которых уходит значительная часть времени, обычно применяется профайлер.
Модуль profile
Этот модуль позволяет проанализировать работу функции и выдать статистику использования процессорного времени на выполнение той или иной части алгоритма.
В качестве примера можно рассмотреть профилирование функции для поиска строк из списка, наиболее похожих на данную. Для того чтобы качественно профилировать функцию difflib.get_close_matches, нужен большой объем данных. В файле russian.txt собрано 160 тысяч слов русского языка. Следующая программа поможет профилировать функцию difflib.get_close_matches:
Листинг
import difflib, profile
def print_close_matches(word):
print "\n».join(difflib.get_close_matches(word + "\n», open(«russian.txt»)))
profile.run(r'print_close_matches(«профайлер»)')
При запуске этой программы будет выдано примерно следующее:
Листинг
провайдер
трайлер
бройлер
899769 function calls (877642 primitive calls) in 23.620 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 23.610 23.610 <string>:1(?)
1 0.000 0.000 23.610 23.610 T.py:6(print_close_matches)
1 0.000 0.000 0.000 0.000 difflib.py:147(__init__)
1 0.000 0.000 0.000 0.000 difflib.py:210(set_seqs)
159443 1.420 0.000 1.420 0.000 difflib.py:222(set_seq1)
2 0.000 0.000 0.000 0.000 difflib.py:248(set_seq2)
2 0.000 0.000 0.000 0.000 difflib.py:293(__chain_b)
324261 2.240 0.000 2.240 0.000 difflib.py:32(_calculate_ratio)
28317 1.590 0.000 1.590 0.000 difflib.py:344(find_longest_match)
6474 0.100 0.000 2.690 0.000 difflib.py:454(get_matching_blocks)
28317/6190 1.000 0.000 2.590 0.000 difflib.py:480(__helper)
6474 0.450 0.000 3.480 0.001 difflib.py:595(ratio)
28686 0.240 0.000 0.240 0.000 difflib.py:617(<lambda>)
158345 8.690 0.000 9.760 0.000 difflib.py:621(quick_ratio)
159442 2.950 0.000 4.020 0.000 difflib.py:650(real_quick_ratio)
1 4.930 4.930 23.610 23.610 difflib.py:662(get_close_matches)
1 0.010 0.010 23.620 23.620 profile:0(print_close_matches(«профайлер»))
0 0.000 0.000 profile:0(profiler)
Здесь
Из приведенной статистики следует, что наибольшие усилия по оптимизации кода необходимо приложить в функциях quick_ratio (на нее потрачено 8,69 секунд), get_close_matches (4,93 секунд), затем можно заняться real_quick_ratio (2,95 секунд) и _calculate_ratio (секунд).
Это лишь самый простой вариант использования профайлера: модуль profile (и связанный с ним pstats) позволяет получать и обрабатывать статистику: их применение описано в документации.
Модуль timeit
Предположим, что проводится оптимизация небольшого участка кода. Необходимо определить, какой из вариантов кода является наиболее быстрым. Это можно сделать с помощью модуля timeit.
В следующей программе используется метод timeit для измерения времени, необходимого для вычисления небольшого фрагмента кода. Измерения проводятся для трех вариантов кода, делающих одно и то же: конкатенирующих десять тысяч строк в одну строку. В первом случае используется наиболее естественный, «лобовой» прием инкрементной конкатенации, во втором — накопление строк в списке с последующим объединением в одну строку, в третьем применяется списковое включение, а затем объединение элементов списка в одну строку: