Язык программирования Python
Шрифт:
Листинг
from timeit import Timer
t = Timer("""
res = ""
for k in range(1000000,1010000):
res += str(k)
«"")
print t.timeit(200)
t = Timer("""
res = []
for k in range(1000000,1010000):
res.append(str(k))
res = ",".join(res)
«"")
print t.timeit(200)
t = Timer("""
res = ",".join([str(k) for k in range(1000000,1010000)])
«"")
print t.timeit(200)
Разные версии Python дадут различные результаты прогонов:
Листинг
# Python 2.3
77.6665899754
10.1372740269
9.07727599144
# Python 2.4
9.26631307602
9.8416929245
7.36629199982
В
Если требуются более точные результаты, рекомендуется использовать метод repeat(n, k) - он позволяет вызывать timeit(k) n раз, возвращая список из n значений. Необходимо отметить, что на результаты может влиять загруженность компьютера, на котором проводятся испытания.
Оптимизация
Основная реализация языка Python пока что не имеет оптимизирующего компилятора, поэтому разговор об оптимизации касается только оптимизации кода самим программистом. В любом языке программирования имеются свои характерные приемы оптимизации кода. Оптимизация (улучшение) кода может происходить в двух (зачастую конкурирующих) направлениях: скорость и занимаемая память. В условиях достатка оперативной памяти приложения обычно оптимизируют по скорости. При оптимизации по времени программы для одноразового вычисления следует иметь в виду, что в общее время решения задачи входит не только выполнение программы, но и время ее написания. Не стоит тратить усилия на оптимизацию программы, если она будет использоваться очень редко.
Следует учитывать, что программа, реализующая некоторый алгоритм, не может быть оптимизирована до бесконечно малого времени вычисления: используемый алгоритм имеет определенную временную сложность и программу, основанную на слишком сложном алгоритме, существенно оптимизировать не удастся. Можно попытаться сменить алгоритм (хотя многие задачи этого сделать не позволяют) или ослабить требования к решениям. Иногда помогает упрощение алгоритма. К сожалению, оптимизация кода, как и программирование — задача неформальная, поэтому умение оптимизировать код приходит с опытом.
Если скорость работы программы при большой длине данных не устраивает, следует поискать более эффективный алгоритм. Если же более эффективный алгоритм практически нецелесообразен, можно попытаться провести оптимизацию кода.
Собственно, в данном примере для модуля timeit уже показан практический способ нахождения оптимального кода. Стоит также отметить, что с помощью профайлера нужно определить места кода, отнимающие наибольшую часть времени. Обычно это действия, выполняемые в самом вложенном цикле. Можно попытаться вынести из цикла все, что можно вычислить в более внешнем цикле или вообще вне цикла.
В языке Python вызов функции является относительно дорогостоящей операцией, поэтому на критичных по скорости участках кода следует избегать вызова большого числа функций.
В некоторых случаях работу программы на Python можно ускорить в несколько раз с помощью специального оптимизатора (он
Листинг
import psyco
psyco.full
Правда, некоторые функции не поддаются «компиляции» с помощью psyco. В этих случаях будут выданы предупреждения. Посмотрите документацию по psyco с тем, чтобы узнать ограничения в его использовании и способы их преодоления.
Еще одним вариантом ускорения работы приложения является переписывание критических участков алгоритма на языках более низкого уровня (С/С++) и использование модулей расширения из Python. Однако эта крайняя мера обычно не требуется или модули для задач, требующих большей эффективности, уже написаны. Например, для работы с растровыми изображениями имеется прекрасная библиотека модулей PIL (Python Imaging Library). Численные расчеты можно выполнять с помощью пакета Numeric и т.д.
Pychecker
Одним из наиболее интересных инструментов для анализа исходного кода Python программы является Pychecker. Как и lint для языка C, Pychecker позволяет выявлять слабости в исходном коде на языке Python. Можно рассмотреть следующий пример с использованием Pychecker:
Листинг
import re, string
import re
a = «a b c»
def test(x, y):
from string import split
a = «x y z»
print split(a) + x
test(['d'], 'e')
Pychecker выдаст следующие предупреждения:
Листинг
badcode.py:1: Imported module (string) not used
badcode.py:2: Imported module (re) not used
badcode.py:2: Module (re) re–imported
badcode.py:5: Parameter (y) not used
badcode.py:6: Using import and from … import for (string)
badcode.py:7: Local variable (a) shadows global defined on line 3
badcode.py:8: Local variable (a) shadows global defined on line 3
В первой строке импортирован модуль, который далее не применяется, то же самое с модулем re. Кроме того, модуль re импортирован повторно. Другие проблемы с кодом: параметр y не использован; модуль string применен как в операторе import, так и во from–import; локальная переменная a затеняет глобальную, которая определена в третьей строке.
Можно переписать этот пример так, чтобы Pychecker выдавал меньше предупреждений:
Листинг
import string
a = «a b c»
def test(x, y):
a1 = «x y z»
print string.split(a1) + x
test(['d'], 'e')
Теперь имеется лишь одно предупреждение:
Листинг
goodcode.py:4: Parameter (y) not used
Такое тоже бывает. Программист должен лишь убедиться, что он не сделал ошибки.
Исследование объекта
Даже самые примитивные объекты в языке программирования Python имеют возможности, общие для всех объектов: можно получить их уникальный идентификатор (с помощью функции id), представление в виде строки — даже в двух вариантах (функции str и repr); можно узнать атрибуты объекта с помощью встроенной функции dir и во многих случаях пользоваться атрибутом __dict__ для доступа к словарю имен объекта. Также можно узнать, сколько других объектов ссылается на данный с помощью функции sys.getrefcount. Есть еще сборка мусора, которая применяется для освобождения памяти от объектов, которые более не используются, но имеют ссылки друг на друга (циклические ссылки). Сборкой мусора (garbage collection) можно управлять из модуля gc.