Недавно на форуме случился топик посвященный извечной проблеме всех питонистов -- кодировкам. Человек жаловался на то, что у него в программе получаются строчки вида:
u'\xd0\x9a\xd1\x83\xd1\x80\xd1\x83\xd0\xbc\xd0\xbe\xd1\x87'
Вы заметили что что-то не так? И я вот. Строчки как бы уникодные, но внутри них закодированные utf-8 байты. Что-то pдесь не так. Разбираясь дальше и потребовав скрипт, которые такое генерирует, становится понятно, что данные берутся из веба. Вполне обычным способом через urllib и потом скармливаются в lxml.html для разбора. Поскольку urllib оперирует только байтовыми строками, то он не мог их так превратить в уникод, а значит во всем виноват lxml.
Вообще lxml очень крутая библиотека - и быстрая, и функциональная, и умеет мимикрировать интерфейсом под ElementTree, и взаимодействовать с BeatifulSoup. Она давно уже пользуется популярностью у питонистов, когда надо как-то удобно работать с xml.
Но тут немного другой случай. Тут используется парсер html. И именно в нем происходят эти неприятные метаморфозы со строками.
Я решил понять в чем же всё-таки и дело и как побороть такое поведение.
Для начала, я сходил на http://yandex.ru/ и посмотрел что за html там отдается. Кодировка контента utf8. Сразу что бросилось в глаза -- это ...
Примерно год назад, во время массовой истерии по поводу питонячих и не очень асинхронных серверов, я, пытаясь составить своё мнение обо всем об этом, набрел на интересную библиотеку - greenlet. На которой базируется сетевая библиотека eventlet, а на ней всю очередь WSGI веб-сервер spawning.
Как видно из названия, она позволяет делать легкие треды - гринлеты в обычном не stackless питоне. Я написал небольшой скрипт "на попробовать":
#!/usr/bin/env python
from greenlet import greenlet
def dispatch():
while True:
for w in workers:
w.switch()
print 'dispatch'
if not all(workers):
break
d = greenlet(dispatch)
def worker(name):
for i in range(5):
print name, i
if i % 2 == 0:
print 'switch'
d.switch()
workers = [
greenlet(lambda: worker('foo')),
greenlet(lambda: worker('bar'))
]
print 'start'
d.switch()
print 'done'
вывод:
start
foo 0
switch
dispatch
bar 0
switch
dispatch
foo 1
foo 2
switch
dispatch
bar 1
bar 2
switch
dispatch
foo 3
foo 4
switch
dispatch
bar 3
bar 4
switch
dispatch
done
и вот недавно набрел на него. Правда прекрасно? Такой простор для творчества - передача контекста выполнения в произвольное место с сохранением состояния. Некий намек на потусторонний call/cc из "других" языков.
Ещё тогда у меня родилась идея где это можно ...
Pip это альтернатива easy_install, а как говорят сами разработчики - замена.
Как известно easy_install только часть глыбы под названием setuptools. Много копий сломано по поводу нужно ли такие двухголовое чудовище, которое позволяет продвинутым образом собирать питонячьи пакеты и их устанавливать. Или достаточно стандартного distutils. Вот тут pip выступает как противоположность, говоря - я сборкой пакетов не занимаюсь, а только их ставлю.
Pip может поставить любой пакет собранный при помощи distutils. Причем только source-пакет - никаких бинарных яиц ему и прочих setuptools'овых прибамбасов.
Эта концептуальная простота во многом помогла pip постепенно выйти на уровень широко используемого инструмента в питон-сообществе. Благо и пользоваться им максимально просто:
# pip install wna
где wna - это некий пакет (для примера я возьму код своего блога).
Так же преимуществом pip безусловно является более полезный вывод информации о процессе установки и репортинг ошибок во всяких непредвиденных ситуациях.
Помимо уже собранных source-пакетов pip может брать исходники пакетов из систем контроля версий. Поддерживаются subversion, mercurial, git, bazaar. Делая checkout и устанавливая через python setup.py с ключом devel, дает возможность иметь в папке /src/packet_name/ исходный код и при необходимости редактировать его.
# pip install -e hg+http://bitbucket.org/daevaorn/turbion/#egg=turbion
Где egg=turbion говорит pip чтобы он сделал checkout ...
Много у вас разных питонячих проектов работают на одном сервере? У меня вот да. Все ли они использую одни и те же библиотеки или версии библиотек? Нет. Так как сделать, чтобы можно было удобно поддерживать всё это многообразие?
Давным дано я предлагал один вариант - использовать svn:externals и таскать зависимости (код сторонних библиотек и приложений) с собой. Но такой способ очень ограничен в своем применении. Вы должны использовать VCS (subversion или иные поддерживающие аналогичные концепции) для развертывания проекта на сервере и все зависимости тоже должны быть доступны в той же системе контроля версий. Но так случается очень редко. Да и потом далеко не всегда VCS вообще используются для выкладки проектов. Пакетные системы во многих случаях удобней.
Создание изолированных окружений задача довольно давнишняя. И в питонячем мире решается разными способами уже давно. Одним из инструментов является - virtualenv.
Создать окружение просто:
virtualenv myenv
После выполнения этой команды создается директория myenv в который находится некое подмножество unix-like корневой файловой системы. В директории myenv/bin будет лежать бинарник питона, и несколько дополнительных скриптов. В myenv/lib - дерево каталогов, повторяющее оное у текущего установленного питона в системе.
Для того чтобы питонячий код работал в этом окружении, его надо запускать, используя myenv/bin/python, или подключив ...
В прошлом посте я рассказал, как можно изменять порядок полей в форме (ну и в модели). Там я чуть-чуть затронул описание низкоуровневой реализации механизма выставления по порядку полей (атрибутов), соответствующему тому как эти поля объявлены в классе.
Откуда вообще взялась необходимость в каких-то хитростях для определения этого порядка? Да потому что, после того как класс создан, к его атрибутам можно легко обратиться через __dict__, но как известно обычный питонячий dict - unordered. Т.е. из-за своей реализации в языке, ключи в нем упорядочены не по тому как они в него попадали, а случайно (на самом деле не совсем случайно, но для нас это не интересно). А следовательно информация о порядке объявления атрибутов класса безвозвратно потеряна. Существует распространенный способ обхода этой проблемы, который активно используется в нескольких компонентах джанги - в моделях и формах.
Классический способ
Для заданного класса, который будет классом атрибутов, нуждающихся в упорядочивании (для джанги это models.Field и forms.Field), заводится "статический" счетчик, который, обновляясь при каждом создании объекта данного класса, хранит порядковый номер этого самого объекта. В дальнейшем по этому номеру поля сортируются и заносятся в какой-либо упорядоченный контейнер - например в SortedDict.
Причем не важно количественное значение счетчика. Главное, что поля объявленнные раньше имеют меньший порядковый номер. Поэтому ...