Простой Python — страница 43 из 66

Отладка кода вдвое сложнее, чем его написание. Так что если вы пишете код настолько умно, насколько можете, то вы по определению недостаточно сообразительны, чтобы его отлаживать.

Брайан Керниган (Brian Kernighan)

Всегда тестируйте свой код. Чем лучше тесты, тем меньше вам предстоит работы в дальнейшем. Однако ошибки случаются, и их нужно исправлять. Самый простой способ выполнять отладку в Python — построчно выполнять код. Полезно отображать результат работы функции vars(), которая извлекает значения ваших локальных переменных, включая аргументы функций:

>>> def func(*args, **kwargs):

…·····print(vars())

>>> func(1, 2, 3)

{'args': (1, 2, 3), 'kwargs': {}}

>>> func(['a', 'b', 'argh'])

{'args': (['a', 'b', 'argh'],), 'kwargs': {}}

Как вы уже знаете из раздела «Декораторы» главы 4, декоратор может вызывать код, располагающийся до или после функции, не модифицируя код внутри самой функции. Это значит, что вы можете использовать декоратор, чтобы выполнить какое-либо действие до или после вызова любой функции, а не только тех, которые написали вы. Определим декоратор dump, который позволяет вывести на экран входные аргументы и выводимые значения любой функции по мере ее вызова (дизайнеры знают, что выходные данные нужно декорировать):

def dump(func):

····"Print input arguments and output value(s)"

····def wrapped(*args, **kwargs):

········print("Function name: %s" % func.__name__)

········print("Input arguments: %s" % ' '.join(map(str, args)))

········print("Input keyword arguments: %s" % kwargs.items())

········output = func(*args, **kwargs)

········print("Output: ", output)

········return output

····return wrapped

Перейдем к декорируемой части. Это функция с именем double(), которая принимает именованные или безымянные числовые аргументы и возвращает их удвоенные значения в списке:

from dump1 import dump

@dump

def double(*args, **kwargs):

····"Double every argument"

····output_list = [2 * arg for arg in args]

····output_dict =··{ k:2*v for k,v in kwargs.items() }

····return output_list, output_dict

if __name__ == '__main__':

····output = double(3, 5, first=100, next=98.6, last=-40)

Запустите пример:

$ python test_dump.py

Function name: double

Input arguments: 3 5

Input keyword arguments: dict_items([('last', -40), ('first', 100),

('next', 98.6)])

Output: ([6, 10], {'last': -80, 'first': 200, 'next': 197.2})

Отлаживаем с помощью pdb

Эти приемы полезны, но иногда ничто не сможет заменить настоящий отладчик. Большинство IDE содержат отладчики, чьи возможности и пользовательские интерфейсы могут варьироваться. В этом разделе я опишу использование стандартного отладчика Python pdb (https://docs.python.org/3/library/pdb.html).


Если вы запускаете программу с флагом — i, при ее неудачном завершении Python вернет вас в интерактивный интерпретатор.


Рассмотрим программу с ошибкой, которая зависит от входных данных, — такую ошибку может быть особенно трудно найти. Это реальная ошибка, возникшая в ранние дни программирования, она довольно долго сбивала с толку программистов.

Мы собираемся считать файл, содержащий названия стран и их столиц, разделенные запятыми, и вывести их на экран в формате «столица, страна». Они могут быть неправильно выделены прописной буквой, поэтому нам нужно исправить это при выводе на экран. В файле также могут быть лишние пробелы, вам нужно избавиться и от них. Наконец, несмотря на то что было бы логично считать весь файл до конца, по какой-то причине наш менеджер сказал нам остановиться, если мы встретим слово quit (состоящее из смеси прописных и строчных букв). Так выглядит файл с данными:

France, Paris

venuzuela,caracas

··LithuniA,vilnius

·····quit

Разработаем алгоритм (способ решения задачи). Это псевдокод, он выглядит как программа, но является лишь способом выразить логику простым языком до преобразования его в настоящую программу. Одна из причин, по которым программисты любят Python, — он выглядит очень похожим на псевдокод, поэтому его не так трудно преобразовать в рабочую программу:

для каждой строки в текстовом файле

считать строку

удалить пробелы в начале и конце строки

если найдена строка "quit" в строке, записанной в нижнем регистре

остановиться

иначе

разделить страну и столицу символом запятой

удалить пробелы в начале и конце

записать страну и столицу с прописной буквы

вывести на экран столицу, запятую и страну

Нам нужно удалить из имен начальные и конечные пробелы, поскольку это является требованием к программе. Аналогично мы поступаем со сравнением со строкой quit и записью названий страны и города с прописной буквы. Имея это в виду, напишем файл capitals.py, который точно будет работать корректно:

def process_cities(filename):

····with open(filename, 'rt') as file:

········for line in file:

············line = line.strip()

············if 'quit' in line.lower():

················return

············country, city = line.split(',')

············city = city.strip()

············country = country.strip()

············print(city.title(), country.title(), sep=',')

if __name__ == '__main__':

····import sys

····process_cities(sys.argv[1])

Протестируем программу с помощью файла, созданного ранее. На старт, внимание, марш:

$ python capitals.py··cities1.csv

Paris,France

Caracas,Venuzuela