воскресенье, 31 октября 2010 г.

Переходим на Python 3. Новый print.

С недавнего времени решил начинать осваивать Python 3, а свои наблюдения и опыт изучения писать в блог, да так, чтобы и самому структурировать новые знания и читателям было интересно. Так что милости прошу к первой заметке о новой реализации стандартного вывода.

Первое знакомство
Наверное, первое, что бросается в глаза при переходе на Python 3, это новая реализация механизма стандартного вывода - оператор print был заменен на функцию print(). Зачем? Такое вот, казалось бы, незначительное, от нечего делать, изменение. Что ж, следует исследовать проблему поподробнее.

История
Нарекания на реализацию вывода как оператора print не один раз возникали и бурно обсуждались в списке рассылке (Python-dev mailing list), и вылились все они в один единый документ - PEP3105. В данном документе сразу же можно увидеть список недостатков оператора print. Изложу их вкратце в вольном переводе и своих комментариях в скобках:
  1. print - единственная операция уровня приложения, под которую выделен оператор, и, в принципе он там не нужен (сильно, так сказать, много чести для операции вывода).
  2. На некотором этапе разработки приложение возникает необходимость в замене вывода в стандартный поток на что-нибудь более умное, вроде лога. Сейчас же приходится обходится другими средствами, доходит и до использования >>stream (как по мне, то это никак не Pythonic-way).
  3. На днный момент нет удобного пути вывода нескольких объектов с разделителем отличным от пробела.
По этому поводу в Python 3 оператор print был заменен функцией print(). На самом деле очень удобно. Приведу несколько иллюстраций в сравнении на Python 2.6 и Python 3.

Практические примеры
Пример 1. Вывод одного объекта.

Python 2.6
>>> x = 1
>>> print x
1
Python 3.1.1
>>> x = 1
>>> print(x)
1
Интересный пример. Расхвалили новый print, а тут на тебе - ни одного плюса, только один минус - нужно тянуться к скобкам на клавиатуре =). Впрочем, у кого есть IDE с настроенной горячей клавишей на вставку вывода (например, TextMate), особо не почувствует.

Пример 2. Вывод трех объектов.

Python 2.6
>>> x1 = 1
>>> x2 = 2
>>> x3 = 3
>>> print x1, x2, x3
1 2 3
Python 3.1.1
>>> x1 = 1
>>> x2 = 2
>>> x3 = 3
>>> print(x1, x2, x3)
1 2 3
Так, что у нас здесь. Вроде бы никаких плюсов опять.

Пример 3. Вывод трех объектов с разделителем ";".


Python 2.6
>>> print x1, ';', x2, ';', x3
1 ; 2 ; 3
Коротко, но как-то не совсем ожидаемый результат. Между выводимым значением и точкой с запятой стоят пробелы. Надо что-то менять. Например, так:
>>> print '%s;%s;%s' % (x1, x2, x3)
1;2;3
Опять не то. А что если параметров будет 10? Не строчить же нам 10 %s-ов. Давайте так:
>>> print ('%s;'*3)[:-1] % (x1, x2, x3)
1;2;3
Python 3.1.1
>>> print(x1, x2, x3, sep=';')
1;2;3
Что же мы видим здесь? Задача решается с помощью как старого подхода, так и нового. Но, согласитель, оператор справляется с задачей как-то уж очень некрасиво, да и мозги надо хоть как-то включить, чтоб такое вывести, не то что просто задать параметр sep.

Пример 3. Решить задачу из примера 2, только без перевода строки в конце.
Вот тут-вот оператор print вообще дает сбой. Он этого попросту не умеет, а иногда для удобства такое нужно (самому несколько раз требовалось). Что ж, в Python 2.6 можно реализовать так:
Python 2.6
>>> import sys
>>> sys.stdout.write(('%s;'*3)[:-1] % (x1, x2, x3))
1;2;3>>>
Python 3.1.1
>>> print(x1, x2, x3, sep=';', end='')
1;2;3>>>
А это выглядит уже вообще не прикольно. Мало того, что задачу решили не через print, так еще нужно импортировать дополнительный модуль, и чего-то там делать, тогда как по идее задача должна решаться через оператор print, так как это задача стандартного вывода. Гораздо веселее, как видите, это выглядит в Python 3 - просто еще один параметр.

Ладно, не буду уже приводить пример с перенаправлением вывода в поток или файл (ну, в конечном счете, для Python это одно и тоже - file-like object), так как в Python 2.6, по сравнении с Python 3 это покажется действительно страшным зрелищем.

Подведем итоги
Надеюсь, я убедил вас, что замечательная функция print, пойдет на пользу как языку, так и его разработчикам (себя я убедил =)). Итак, что мы имеем? А имеем мы функцию print:
def print(*args, sep=' ', end='\n', file=sys.stdout)
  • *args - выводимые объекты
  • sep - разделитель между выводимыми значениями (по умолчанию - пробел)
  • end - символ, которым заканчивается вывод (по умолчанию - символ новой строки)
  • file - file-like объект, в который мы можем перенаправить вывод, который по умолчанию производится в sys.stdout
Не попадайтесь
Заметил один замечательный побочный эффект работы с разными версиями Python, связанный с новым print. Если в Python 3 вы по ошибке напишите
>>> print x1, x2, x3
получите синтаксическую ошибку. Если же в Python 2.x, напишете
>>> print(x1, x2, x3)
(1, 2, 3)
получите вывод кортежа. Эффект не критичный, но все-же присутствует.




вторник, 26 октября 2010 г.

Мои сумбурные впечатления от PyCon UA 2010


Распишу, пожалуй, по пунктам

1. Очень понравились доклады Андрея Светлова. Минусами были только проблемы с подключением ноута к проектору, и то, что его быстро согнали со сцены за недостатком времени smile. Очень познавательный доклад по внутренностям python, а так же очень ценные заметки в докладе по многопоточности (думаю, тем, кто занимается этим, было полезно послушать, а тем более новичкам).

2. Интересный доклад по Good API Design от Armin Ronacher. Считаю, что доклад он оформил очень правильно, показал как API делать не-надо, и как делать надо на примерах. То есть оформил основные принципы и разжевал их.

3. Комрад из Техаса с докладом по GeoDjango очень хорошо подготовился в плане использования украинских названий в классах и локациях (типа class Oblast, Київ и т.д.), в принципе показал такое себе шоу с Django + Google Earth, рассказал о стандартах геокодирования. Довольно интересно, если начинать этим заниматься, то он дал очень хорошие указатели, куда двигаться. К слову, он же является core-developer этого самого GeoDjango.

4. Andrew Godwin дал в принципе хорошую оценку использованию БД в разных случаях. Тут согласен, больше для тех, кто с БД сталкивался мало.

5. Товарищи из Киевской студии web-riders это было что-то. Тут настоятельно советуем посмотреть видео, обещают в течении 2-х недель.

6. 2 Доклада по GAE были вообще глотком свежего воздуха. Сначала Кашкин расхвалил GAE, показал image-board, который сразу же жестко затролили, разбавив обстановку smile, а потом другой докладчик погнобил GAE Datastore (ну, так получилось smile ).

7. Доклад по HTML5 был больше обзорным, и о том, куда двигается это все дело, в том числе с позиции Гугла, так как Michael Mahemoff там работает.

8. Насчет монго, цифры были, только не впечатляющие. То есть 4 млн. документов в 100 коллекциях при средненькой нагрузке работает. А как дальше - опыта у них пока нет.

IMHO:
1. Были доклады интересные, были и неинтересные.
2. Не понравилось то, что куча Django, такое впечатление, что нынче на питоне кроме Django-сайтов больше ничего и не делают.
3. Lightning-talks довольно весело, особенно Python после литра, такие себе just-for-fun поделки.
4. В целом - отлично.

четверг, 21 октября 2010 г.

Как узнать параметры процессора в Windows XP с помощью Python

Собственно, я нашел два способа, как это сделать.

Способ 1 - через реестр
Информацию о вашем CPU можно получить, прочитав ветку HKEY_LOCAL_MACHINE\HARDWARE\
\DESCRIPTION\\System\\CentralProcessor\\0. Вот как это выглядит:








Получить доступ к реєєстру в Python очень легко с помощью модуля _winreg, который входит в стандартную библиотеку. Вот, что нужно сделать, чтобы получить тактовую частоту процессора:

import _winreg
handle = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, '\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0')
value = _winreg.QueryValueEx(handle, '~MHz')

Способ 2 - библиотека WMI
Библиотека WMI (Windows Measurement Instruments), написанная Тимом Голденом, позволяет решить эту задачу гораздо быстрее.

>>> import wmi
>>> c = wmi.WMI()
>>> c.Win32_Processor()[0].MaxClockSpeed
2812
>>> c.Win32_Processor()[0].NumberOfCores
2

На самом деле то значение, которое мы получаем в Win32_Processor()[0] - это объект класса, спецификацию которого можно почитать, обратившись к MSDN. Таким образом, получаем возможность получить гораздо больше информации.

вторник, 19 октября 2010 г.

Работа с Git в Windows через https

Пробема
Понадобилось мне получить доступ на коммит к проекту на github, а линуксовая виртуальная машина, в среде которой я работал, скоропостижно скончалась. Так как времени разбираться с машиной, а тем более переустанавливать не было - выбрал более быстрое решение - работать с Git под Windows через Https. Так как msys/Git у меня уже был установлен, а проект вытащен из репозитария в режиме read-only, вот что получилось с первого раза:

D:\wishmaster\wishmaster>git remote rm origin
D:\wishmaster\wishmaster>git remote add origin https://....
D:\project\project>git pull
Password:
error: error setting certificate verify locations:
CAfile: /bin/curl-ca-bundle.crt
CApath: none
while accessing https://SSPkrolik@github.com/SSPkrolik/wishmaster.git/info/refs

fatal: HTTP request failed

Не сильно радует. Ну, из этой ситуации есть два выхода.

Выход 1 - отказываемся от проверки сертификата
Для этого делаем следующее:
git config --global http.sslverify "false"
Вуаля. Все работает.

Выход 2 - говорим где лежит сертификат
Для этого делаем следующее:
git config --system http.sslcainfo <путь к сертификату>
Этот сертификат должен лежать в той же папке, в которую Вы установили git. Скорее всего это C:\Program files\Git.

понедельник, 18 октября 2010 г.

Скрещиваем pywinauto и py2exe

А начиналось все с GUI-приложения
Как-то понадобилось мне в приложении делать автоматизированную работу с внешними программами, а конкретно с Adobe Reader. Задача в следующем. При выполнении поиска в Python-приложении (то есть, вводим текст в wxPython GUI) - открывать PDF-файл с помощью Adobe Reader, да не просто открывать, а так, чтобы в строке поиска уже было введено слово, по которому ищется информация, а сам PDF-документ был открыт на первом найденном результате. Ко всему прочему приложение распространяется под Windows-платформы, исполняемые файлы создаются с помощью py2exe.

Но потребовалась некоторая автоматизация действий пользователя
Для решения этой задачи был выбран pywinauto - замечательная библиотека для тестирования GUI-приложений. И хотя в данном случае никакого тестирования нет, с автоматизацией она тоже справляется на ура.

И тут в чудесный мир Python врывается exe-формат
Все отлично отрабатывает, если запускать python скрипт. Проблемы возникают в случае, когда запускается это же приложение собранное с помощью py2exe.

Спасибо py2exe за его чудесную функцию ведения лога. В логе можно увидеть примерно следующее:

Traceback (most recent call last):
File "autorun.py", line 3, in
from gui.wxmain import SplashWindow
File "zipextimporter.pyc", line 82, in load_module
File "gui\wxmain.pyc", line 11, in
Splash window
File "zipextimporter.pyc", line 82, in load_module
File "pywinauto\__init__.pyc", line 28, in
File "zipextimporter.pyc", line 82, in load_module
File "pywinauto\findwindows.pyc", line 37, in
File "zipextimporter.pyc", line 82, in load_module
File "pywinauto\controls\__init__.pyc", line 33, in
File "zipextimporter.pyc", line 82, in load_module
File "pywinauto\controls\win32_controls.pyc", line 37, in
File "zipextimporter.pyc", line 82, in load_module
File "pywinauto\tests\__init__.pyc", line 153, in
File "pywinauto\tests\__init__.pyc", line 137, in __init_tests
ImportError: No module named allcontrols

Решаем проблему
Как видим, существует проблема с импортами в тестах. Проблема решается очень просто. Заходим в c:\Python<версия>\Lib\site-packages\pywinauto\tests\, открываем замечательный файлик __init__.py и комментируем все строки, которые хоть каким-то боком что-то импортируем (с целью экономии глаз и нервов читателя, полный код модуля приводить не буду). Собираем еще раз исполняемый exe-файл, и все работает.

P.S
Проблема у меня возникла в pywinauto при версии Python 2.6. Возможно, в других версиях такой проблемы и не будет.

четверг, 14 октября 2010 г.

wx.PaintDC не перерисовывает картинку

Очень интересный эффект, а точнее ошибка, получилась у меня при отрисовке на форме (wx.Frame) с помощью wx.PaintDC. Вот код события wx.EVT_PAINT, который использовался сначала:

def on_paint(self, evt):
self.dc = wx.PaintDC(self)
self.dc.DrawBitmap(self.background, 0,0, True)

Как вы уже, наверное догадались данные манипуляции ведутся с целью отрисовки фона окна, а точнее само окно в форме картинки. Пример можно подсмотреть в wx.Demo -> Miscellaneous -> ShapedWindow. Так вот, такой код приводит к тому, что, если деактивировать окно (произойдет событие ex.EVT_ACTIVATE), и потом опять активировать (это же событие), то контролы внутри окна не отрисуются. Теперь посмотрим на код, который работает правильно:

def on_paint(self, evt):
dc = wx.PaintDC(self)
dc.DrawBitmap(self.background, 0,0, True)

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

четверг, 7 октября 2010 г.

Подводный камень позиционирования окон в wxPython

Представим себе окно (wx.Frame), которое нужно разместить на екране согласно некторому алгоритму, например, отцентрировать. Что мы делаем?

class MyFrame(wx.Frame):
...
def __init__(self):
...
ds = wx.DisplaySize()
self.SetPosition((ds[0] / 2 - self.Size[0] / 2,
ds[1] / 2 - self.Size[1] / 2))
...

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

Часто такие операции происходят неявно, и, следственно, окно позиционируется совершенно неправильно, и об этом следует всегда помнить. Примером такой неявной смены размеров окна может быть, скажем установка сайзера (wx.Sizer):

self.SetSizer(self.main_sizer)
self.Fit()

В этом случае окно "подтянет" свои размеры под сайзер и, соответственно, позиционирование пройдет неправильно.

Особенности промышленного копирования файлов в Python

Столкнулся с задачей копирования файлов в Python. Задача состояла в том, чтобы копировать полностью структуру папок из одного места в другое, запускать программу, а потом удалять все содержимое, которое было скопировано.

Первые шаги

Первое что пришло в голову - использовать для задачи os.walk(). Данная функция делает рекурсивный проход по указаному пути, принимая параметром папку в качестве корня дерева, которое необходимо обойти. Стоит заметить, что здесь важным параметром является topdown, в том случае если вы на каждом шаге хотите предпринимать некие действия (в моем случае - это прогресс бар, который выступает индикатором прохождения процесса корпирвоания) . Это необязательный параметр, который принимает булевое значение, определяющее порядок обхода. Для копирования решил использовать функцию copy модуля shutil, который входит в стандартную библиотеку python. Вуаля, все работает. Удаляем файлы после запуска программы, аналогично обходя дерево только уже в той папке, в которую были скопированы файлы.

Небольшая проблемка

Зачем огород городился? Особенностью данной задачи является то, что файлы необходимо копировать из оптического носителя (CD/DVD-диска). Копировать на жесткий диск необходимо, поскольку некоторые приложения для своей работы используют временые файлы, а также пишут данные в файлы настроект. В чем отличие? А отличие в том, что при ипользовании функции copy файлы копируются вместе с битами доступа, а так как источником является оптический диск, то они обозначаются как "только для чтения".

Решение

Решение довольно простое - использовать функцию copyfile из этого же модуля, который не сохраняет биты доступа. Таким образом файлы с легкостью могут быть удалены.

Знание - сила

Модуль shutils обладает также достаточно удобными и полезными функциями массовой обработки файлов, как, например, copytree, или rmtree, которые позволяют отказаться от использования os.walk(). В данном случае copytree использовать не получится, так как нельзя вставить дополнительный код на этапе копирования отдельного файла, а rmtree очень даже хорошо сокращает количество кода.

Выводы

Знание - сила. Перед решением задачи делаем более глубокое исследование готовых решений и инструментов и не полагаемся на уже приобретенные знания.

среда, 6 октября 2010 г.

Django: подлый request.is_ajax()

Наверное все, ну, или почти все Django-разработчики сталкивались с потребностью использовать Ajax в своих проектах. Нет, я не буду рассказывать об ajax в Django вообще, а только о замечательном методе is_ajax() объекта request, который, как известно, передается параметром в обработчик вида. Насколько мне известно по своему опыту и по опыту моих знакомых часто с ним возникает один замечательный казус. Давайте взглянем на следующие строчки кода:


def my_view(request, *args, **kwargs):
...
if request.is_ajax:
...


def my_view(request, *args, **kwargs):
...
if request.is_ajax():
...


Как вы успели догадаться, правильной является вторая запись. Примечательно то, что название метода так и подкупает подумать, что это не метод, а свойсто, то бишь @property, а первый вариант всегда будет возвращать True в условии. Не попадайтесь =).
В этом гаджете обнаружена ошибка