Архив метки: Python

Задача о путешествии шахматного коня

Задача о ходе коня — задача о нахождении маршрута шахматного коня, проходящего через все поля доски по одному разу.

Эта задача известна по крайней мере с XVIII века. Леонард Эйлер посвятил ей большую работу «Решение одного любопытного вопроса, который, кажется, не подчиняется никакому исследованию» (датируется 26 апреля 1757 года).

Читать

Pythons Innards

Перечитываю снова Pythons Innards (Yaniv Aknin).

Это — серия из нескольких статей о том, как устроен CPython изнутри.

  • Интерпретатор байткода
  • Что из себя представляет этот самый байткод
  • Что такое стек в понятии CPython, и как оно работает
  • Как устроены дескрипторы, слоты и классы на нижнем уровне
  • Пространства имен (namespaces)

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

Вдобавок очень хочу посоветовать Ely Bendersky с его Python Internals. Эта серия статей тоже посвящена «внутреннему устройству» и прекрасно сочетается с тестами Yaniv Aknin.

  • Как создаётся объект (в деталях)
  • Что такое class и чем отличается от type
  • Как именно происходит вызов callable
  • И т.д. (symbol tables, например)

На самом деле подобного рода информации очень немного. Документация хорошо описывает CPython C API но не рассказывает о деталях, о том как это всё работает.

У меня до C кода дело доходило разве до обсуждения реализации GIL насколько я помню.

Если есть еще интересные статьи по внутреннему устройству — пишите в комментариях, я добавлю сюда. Наверняка что-то запамятовал, но в целом тема раскрыта очень скудно.

Так что если хотите узнать «как оно работает на самом деле» — читайте  статьи по ссылкам.

UPD.
По устройству типов данных:
Строки
Целые числа
Списки
Словари (для версий Python <3.3)Лекция Larry Hastings (release manager для Python 3.4, между прочим) с US PyCon 2012 на рассматриваемую тему.

Автор: Andrew Svetlov

Поиск первого вхождение подстроки. Решение в лоб.

Поиск информации — одно из основных использований компьютера. Одна из простейших задач поиска информации — поиск точно заданной подстроки в строке. Тем не менее, эта задача чрезвычайно важна — она применяется в текстовых редакторах, СУБД, поисковых машинах…

Читать

Алгоритм Кнута-Морриса-Пратта (КМП)

Алгоритм был разработан Кнутом (Knuth) и Праттом (Pratt) и независимо от них Моррисом (Morris) в 1977 г.

Он относится к «правильным» подходам решения поставленной задачи, в отличии от тривиального подхода, рассмотренного ранее.

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

Однако я нашел пару статей, которые достаточно информативны, они приведены в ссылках и рекомендуемы для ознакомления.

Читать

Использование try-finally

Хочу обратить внимание на маленькую особенность написания конструкции try-finally.

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

Где-то (наверное, в конструкторе класса) мы создали объект блокировки:

self.locker = threading.RLock()

Затем в каком-то методе мы пытаемся использовать эту блокировку в try-finally statement. Да, я знаю что RLock поддерживает context manager protocol и может использоваться в with statement. Так будет даже лучше, но мы сейчас говорим о другом варианте использования.

try:
self.locker.acquire()
do_some_work()
finally:
self.locker.release()

В чём ошибка? .acquire() может выбросить исключение. Блокировка не будет захвачена и попытка её освободить в .release() выбросит новое (другое) исключение. Что крайне нежелательно. Особенно в python 2.x, где нет цепочек исключений. Т.е. ошибка в .acquire() будет просто скрыта, понять в чём было дело невозможно.

Правильно писать так:

self.locker.acquire()
try:
do_some_work()
finally:
self.locker.release()

Если было исключение в .acquire() — то блокировка не захвачена и освобождать её не нужно. Пусть обработка исключения разворачивается своим ходом, .release() в finally block совершенно не нужен.

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

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

Это замечание относится к любому коду, выполняемому в finally block.

Переменные, блокировки, захват ресурсов, открытие файлов и т.д. должны быть выполнены перед try.

P.S.

На открытие файлов хочу обратить особое внимание как на самый частый случай. Куда более частый чем работа с многопоточностью. Правильно писать:

f = open('filename')
try:
f.read()
finally:
f.close()

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

Автор: Andrew Svetlov