wiz 16.12.2010 00:45

Python«Практическая» задачка для начинающих

Периодически в каналах и сайтах новички просят дать им простую задачку, но чтобы она была полезной. Что меня последнее время коробит в «изучении языков» в институтах и всяких учебниках, это то, что там учат буквально синтаксису языка и очень часто упускают «инженерный» аспект программирования в целом.

Поэтому вот такая задачка для питона: напишите реализацию сортировки quicksort.

Я не буду тут рассказывать этот алгоритм, найдите его сами. Это и будет первым уроком. Гугл ваш другл отныне и до конца статьи.
Замечу только, что не надо использовать list comprehensions, это очень неэффективно.

Но это ещё не все. Самое интересное начинается потом. Ведь программирование, как оно преподаётся это прямо таки изобразительное искуство — пара мазков и на холсте появляется ваша Нетленка. Но архитектура тоже была дисциплиной ИЗО, до тех пор пока не рухнул мост, построеный в таком стиле.


Положите свою функцию под названием sort в модуль quicksort.py Она должна получать список и сортировать его на месте. Внимательно относитесь к «контракту» функции. Не добавляйте аргументы и не выдавайте лишнее. Об этом ещё будет позже.

Положите модуль в каталог проекта и поместите его в систему контроля версий. Я рекомендую bzr, как самую гуманную. Потом попробуете остальные и перейдёте на какую понравится. В дальнейшем, считаю, что читатель пользуется именно ей.

bzr init — инициализация репозитория в текущем каталоге

$ bzr st — status, отображение изменений:
unknown:
quicksort.py

bzr add quicksort.py — добавление файла в репозиторий чтобы система его запомнила и в дальнейшем следила за ним внимательно.

bzr diff — вывод «диффа», разницы в новом и старом состоянии построчно. Для новых файлов все строки будут отмечены плюсами. Рекомендую также пользоваться cdiff - небольшая подсветочка всегда пригодится для быстрой оценки происходящего.

если всё ок, то

bzr commit — «коммит», запоминание текущего состояния файлов и ведение истории изменений. настоятельно рекомендую указывать файлы вручную и не комитить всё одним большим куском в многофайловом проекте (да, я смотрю на тебя, nvbn). Будьте внимательны при написании сообщения о коммите, если вы пишете, что добавили строку документации, а в диффе видно, что изменилась переменная, это плохое, негодное сообщение. Если дифф большой, то откройте его в соседнем окне и старайтесь впредь коммитить как можно чаще и как можно более мелкие куски кода.

Я, например, описываю изменения в следующем формате:

+ фича добавлена (слово "добавлена" лучше не писать, для этого там плюсик стоит)
- хлам убран (аналогично "убран")
* переименовал функцию ("горизонтальное" изменение типа перемещения с места на место, переименования и т.п.)
! всех людей теперь надо любить см. баг #42 (описание исправленого бага, желательно с номером заявки в системе учёта)
> вася пупкин поработал на благо общества (сообщение о слиянии с кодом из другого репозитория)


Все эти сообщения потом можно посмотреть в bzr log или на сайтах-кодохостингах.

Всегда, всегда пишите код и ведите историю так, как будто вы пишите опенсорс, даже если у вас приватный проект. Потому что, если вы «заметаете грязь под половик», то место под ним у вас скоро закончится а проект будет откровенно бесить. К тому же, код может передаваться другим людям — волонтёрам, коллегам или тем, кто будет тут работать после вас.

После того, как код написан и типа работает, неплохо бы его причесать. Для этого я пользуюсь серией тестов, которые меня всегда готовы поправить:

python -m doctest — доктесты, обычно работают только в самом начале и быстро сменяются юнит-тестами
pychecker -x -Q — быстрый отлов всяких неопределённых переменных и прочих очепяток и не только
pep8 — граммар-нации в мире питона. постоянно ругается на названия переменных, отступы и прочие штуки. Жизненно необходимо когда с кодом работает несколько человек. Просто незаменимо, когда человек только что пришёл из других языков типа жавы.
pylint -d R0903,W0102,W0142 -rn — ещё один “наци”. Очень жёсткая проверка всего и вся. Как и в случае с pychecker ключами некоторые ошибки всё же заглушены. Рекомендую для начала поработать без ключей.

Там же у меня в репе лежит башик для постоянного мониторинга кода: wex quicksort.py 'pry quicksort.py' будет после каждого изменения обстреливать код батареей проверок и зарабатывать на вас фраги.


Самое время сохраниться: bzr st, bzr cdiff, bzr commit — запомните это как молитву и почаще её повторяйте.

В процессе редактирования кода надо его как-то проверять. Постоянно перезапускать интерпретатор и выполнять там вручную код это очень геморно и сильно замедляет процесс. Поэтому на помощь приходят тесты.

Для начала или для всяких левых функций сойдут и доктесты. Попробуйте дописать в докстринг (вы же уже выполнили требования pylint и написали к ней докстринг?) пару тестов для вашей сортировки. Теперь в любой момент вы сможете в одну команду убедиться, что код 1) работает и 2) работает правильно.

Аминь!

Но доктесты для сложных кусков кода быстра становятся некрасивыми, да и неудобно их там держать. На помощь приходят юнит-тесты. А на помощь к ним приходит nosetests.

Чтобы не захламлять основной модуль, вынесем тесты из докстринга в отдельный файл tests.py

В нём надо сделать функцию, которая начинается с test_, не принимает параметров и делает своё дело.

Что же тестировать и как? Тут на сцену снова выходит Его Величество Контракт. По условиям задачи в проверяемую функцию надо передавать список (list) и он должен быть отстортирован.

1
2
3
4
def test_sort():
data = <100,500,88,14,42> # входные данные
assert not quicksort(data) # на выходе не должно быть “мусора”!
assert data == <14, 88, 42, 100, 500> # если условие не выполнено, тест будет завален (failed)



Запускаются они командой nosetests — она сама найдёт все тесты, выполнит их и отчитается. При этом формат проверяющего кода довольно свободный, в отличии от штатного модуля UnitTest, который взят из жавы и поэтому непитоничен.

Но на самом деле
тест вылетит (error) с NameError т.к. quicksort ещё надо импортировать. Иногда полезно различать фэйлы от эрроров. Первое это невыполнение условий тестов, а второе это сбои в работе. И то и другое может быть в случае невыполнения контракта, но причины разные.


И снова, тесты заработали, пора сохраниться. bzr add tests.py, bzr st, bzr cdiff, bzr commit.

Питон такой хороший, что позволяет сортировать одним кодом не только числа, но и строки и даже вложеные списки. Напишите для этого тесты. Если тесты не срабатываются, значит ваш сорт написан неправильно.

Неплохо также натравить батарею проверок и на сами тесты т.к. в них тоже могут быть ошибки и погрешности.

И только когда всё исправно работает, код не режет глаза, проверяет сам себя, лежит на месте можно считать, что задача выполнена.

Теперь было бы неплохо залить его на кодохостинг, можете потренироваться в этом “дома”. Подсказка: для ланчпада команда будет bzr push lp:~юзернейм/+junk/myquicksort

----

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

Можете в коментах кидать ссылки на свои проектики, я гляну и может что-то посоветую. Code review — незаменимая штука, особенно в обучении.


Тэги: bzr doctest python test tutorial unittest
+ 8 -
Похожие Поделиться

berkus 16.12.2010 02:42 #
До кучи: поставить графический плагин для базара (я пользуюсь qbzr) - преимущества в удобном отображении диффов и возможности выбрать отдельные файлы для коммита - почекал нужные файлы, посмотрел, что всё нужное в диффе выводится (qbzr покажет дифф только по выбранным для коммита файлам), тогда можно жмакать коммит. Ну и всякие прелести типа показа лога с ветвлением по бранчам, естественно, включены.
berkus 16.12.2010 02:47 #
Раз уж пошли такие помидоры про код ревью, посоветуй что-то.
berkus 16.12.2010 02:48 #
(Что тестов мало уже знаю)
wiz 16.12.2010 13:09 #
Ну это какбы не совсем учебная задачка (= Я как-то сразу прям и не догнал что там к чему...
Elvis 16.12.2010 08:45 #
Разные задачи для обучения Питону можно брать на сайте CHECKIO.ORG (англ.)
Сайт обладает возможностью автоматической проверки корректности решения - всегда можно узнать, верно выполнили задачу или нет.
wiz 16.12.2010 12:38 #
Это всё совершенно проходит мимо основной идеи поста. Сама поставленная задача это как палочка у мороженого — как бы основное, но кроме непосредственно решения надо ещё сам процесс и окружение поставить правильно. Тоже самое и к заданиям blackraven.
blackraven 16.12.2010 11:44 #
Есть у меня тут под рукой список тестовых заданий - тоже может пригодиться для тренировки.