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

Vilnius,Lithunia

Выглядит отлично! Программа прошла один тест, поэтому отправим ее на производство обрабатывать столицы и страны со всего мира, пока она не ошибется на данном файле:

argentina,buenos aires

bolivia,la paz

brazil,brasilia

chile,santiago

colombia,Bogotá

ecuador,quito

falkland islands,stanley

french guiana,cayenne

guyana,georgetown

paraguay,Asunción

peru,lima

suriname,paramaribo

uruguay,montevideo

venezuela,caracas

quit

Программа завершается после вывода всего пяти строк, несмотря на то что в файле их было 15, как показано здесь:

$ python capitals.py··cities2.csv

Buenos Aires,Argentina

La Paz,Bolivia

Brazilia,Brazil

Santiago,Chile

Bogotá,Colombia

Что случилось? Мы можем продолжать редактировать файл capitals.py, размещая выражения print() в тех местах, где может возникнуть ошибка, но посмотрим, сможет ли нам помочь отладчик.

Для того чтобы использовать отладчик, импортируйте модуль pdb из командной строки, введя — m pdb, например, так:

$ python — m pdb capitals.py cities2.csv

> /Users/williamlubanovic/book/capitals.py(1)()

-> def process_cities(filename):

(Pdb)

Это запустит программу и разместит вас на первой строке. Если вы введете символ с (от слова continue — «продолжить»), программа будет работать, пока не завершится либо естественным образом, либо из-за ошибки:

(Pdb) c

Buenos Aires,Argentina

La Paz,Bolivia

Brazilia,Brazil

Santiago,Chile

Bogotá,Colombia

The program finished and will be restarted

> /Users/williamlubanovic/book/capitals.py(1)()

-> def process_cities(filename):

Программа завершилась нормально, точно так же, как и раньше, когда мы запускали ее вне отладчика. Попробуем запустить ее снова, использовав специальные команды, чтобы сузить место поиска проблемы. Похоже, имеет место логическая ошибка, а не синтаксическая проблема или исключение (из-за них мы бы увидели сообщение об ошибке).

Введите s (step — «шаг»), чтобы пройти по отдельным строкам кода. Это позволит пройти по всем строкам — вашим, стандартной библиотеки и любых других используемых вами модулей. Когда вы применяете команду s, вы также входите во все функции и проходите каждую построчно. Введите n (next — «следующий»), чтобы идти по шагам, но не заходить внутрь функций: когда вы находитесь на строке, где вызывается функция, эта команда выполняет всю функцию и вы оказываетесь на следующей строке. Используйте s, если вы не уверены в том, где конкретно есть проблема, а n — если уверены, что некоторая функция проблем не вызывает, особенно если это длинная функция. Зачастую вы будете проходить построчно весь свой код и пропускать библиотечный, поскольку подразумевается, что он хорошо протестирован. Мы используем s, чтобы начать двигаться от начала программы к функции process_cities():

(Pdb) s

> /Users/williamlubanovic/book/capitals.py(12)()

-> if __name__ == '__main__':

(Pdb) s

> /Users/williamlubanovic/book/capitals.py(13)()

-> import sys

(Pdb) s

> /Users/williamlubanovic/book/capitals.py(14)()

-> process_cities(sys.argv[1])

(Pdb) s

-Call-

> /Users/williamlubanovic/book/capitals.py(1)process_cities()

-> def process_cities(filename):

(Pdb) s

> /Users/williamlubanovic/book/capitals.py(2)process_cities()

-> with open(filename, 'rt') as file:

Введите l (list — «перечислить»), чтобы увидеть следующие несколько строк своей программы

(Pdb) l

··1······def process_cities(filename):

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

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

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

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

··6·····················return

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

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

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

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

11

(Pdb)

Стрелка (->) указывает на текущую строку.

Мы могли бы и дальше применять команды s или n в надежде что-то найти, но давайте использовать одну из главных особенностей отладчика — точки останова. Точка останова останавливает выполнение программы на указанной вами строке. В данном случае мы хотим узнать, почему функция process_cities() вызывает завершение программы до прочтения всех введенных строк. Строка 3 (for line in file:) будет считывать каждую строку входного файла, поэтому она выглядит невинно. Единственное место, где мы можем вернуться из функции до прочтения всех данных, — это строка 6 (return). Поставим точку останова на строке 6:

(Pdb) b 6

Breakpoint 1 at /Users/williamlubanovic/book/capitals.py:6

Далее продолжим выполнение программы до тех пор, пока она либо не достигнет точки останова, либо не завершится обычным образом:

(Pdb) c

Buenos Aires,Argentina

La Paz,Bolivia

Brasilia,Brazil

Santiago,Chile

Bogotá,Colombia

> /Users/williamlubanovic/book/capitals.py(6)process_cities()

-> return

Ага, она остановилась на точке останова в строке 6. Это показывает, что программа хочет завершиться после прочтения страны, которая идет вслед за Колумбией. Выведем значение переменной line, чтобы увидеть, что мы только что считали:

(Pdb) p line

'ecuador,quito'

Что такого особенного… а, забудьте.

Серьезно? Столица называется *quit*o? Наш менеджер не ожидал, что строка quit станет частью входных данных, поэтому показалось логичным использовать ее в качестве контрольного значения (индикатора конца). Вам следует отправиться прямо к нему и сказать все как на духу, я подожду.

Если после этого у вас все еще есть работа, можете просмотреть все точки останова с помощью команды b:

(Pdb) b

Num Type·········Disp Enb···Where

1···breakpoint···keep yes···at /Users/williamlubanovic/book/capitals.py:6

····breakpoint already hit 1 time

Команда l покажет вам строки кода, текущую строку (->) и все имеющиеся точки останова (B). Вызов команды l без аргументов выведет все строки, начиная с точки предыдущего вызова команды l, поэтому включите в вызов опциональный параметр — стартовую строку (в нашем примере начнем с 1):

(Pdb) l 1

··1······def process_cities(filename):

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

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

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

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

··6 B->·················return

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

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

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

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

11

Теперь модифицируем наш тест так, чтобы выполнялась проверка на полное совпадение со строкой quit, без всяких других символов: