Pycrust что это
Перейти к содержимому

Pycrust что это

  • автор:

PyCrust — экранная оболочка для Python

PyCrust – это написанная на wxPython графическая оболочка, которую Вы можете использовать для анализа wxPython-программ.

Почему она названа PyCrust? Когда Патрик О’браен (Patrick O’Brien), используя wxPython, создавал диалоговую Python-оболочку, очевидно название «PyShell» уже было использовано. Вместо него выбор пал на «PyCrust».

PyCrust – это часть большого пакета Py, который включает дополнительные программы со связанными функциональными возможностями, включая PyFilling, PyAlaMode, PyAlaCarte и PyShell.

Общая идея этих программ — комбинация возможностей визуальной графической (point-and-click) среды, интерактивности wxPython и самоуправления (интроспективность) при выполнении. В отличие от любой из Py-программ, которые усиливают эту комбинацию, PyCrust представляет более полную реализацию этой идеи.

PyCrust (последним исправлял пользователь FrBrGeorge 2010-10-20 12:59:55)

  • Неизменяемая страница
  • Комментарии
  • Информация
  • Прикреплённые файлы
  • MoinMoin Powered
  • Python Powered
  • GPL licensed
  • Valid HTML 4.01
  • Page.execute = 0.001s
  • getACL = 0.007s
  • init = 0.001s
  • load_multi_cfg = 0.000s
  • run = 0.225s
  • send_page = 0.208s
  • send_page_content = 0.027s
  • total = 0.226s

Упрощение работы в wxPython при помощи PyCrust

PyCrust – это написанная на wxPython графическая оболочка, которую Вы можете использовать для анализа wxPython-программ.

Почему она названа PyCrust? Когда Патрик О’браен (Patrick O’Brien), используя wxPython, создавал диалоговую Python-оболочку, очевидно название «PyShell» уже было использовано. Вместо него выбор пал на «PyCrust».

PyCrust – это часть большого пакета Py, который включает дополнительные программы со связанными функциональными возможностями, включая PyFilling, PyAlaMode, PyAlaCarte и PyShell.

Общая идея этих программ — комбинация возможностей визуальной графической (point-and-click) среды, интерактивности wxPython и самоуправления (интроспективность) при выполнении. В отличие от любой из Py-программ, которые усиливают эту комбинацию, PyCrust представляет более полную реализацию этой идеи.

В этой главе, мы покажем Вам, что делают PyCrust и связанные с ним программы, и как Вы можете их использовать, чтобы сделать Вашу работу с потоком wxPython более равномерной. Сначала мы поговорим об обычной Python-оболочке, затем собственно о PyCrust, и, наконец, мы охватим остальные программы в пакете Py.

Как взаимодействовать с wxPython-программой?

Неизменная особенность Python в сравнении с другими языками программирования, состоит в том, что его можно использоваться двумя способами: Вы можете запускать на выполнение существующие Python-программы, или же запустить Python в интерактивном режиме командной строки. Выполнение Python в интерактивном режиме подобно диалогу с Python-интерпретатором. Вы вводите строку кода и нажимаете Enter. Python выполняет этот код, выдает ответ и запрашивает у Вас следующую строку. Такой интерактивный режим существенно отличает Python от таких языков, как C++, Visual Basic или Perl. Имея такой интерпретатор, нет необходимости, чтобы сделать простые вещи, писать в wxPython цельную программу. Фактически, Вы можете даже использовать диалоговый Python как настольный калькулятор.

В листинге 4.1 мы запустили Python в режиме командной строки и выполнили небольшие математические вычисления. Python стартует, отображая несколько строк информации, сопровождаемых его основным приглашением (>>>). Когда Вы введёте, что-нибудь, что потребует дополнительных строк кода, Python покажет вторичное приглашение (. ).

Листинг 4.1 Пример интерактивного сеанса Python

 1  $ Python  2  Python 2.3.3 (#1, Jan 25 2004, 11:06:18)  3  [GCC 3.2.2 (Mandrake Linux 9.1 3.2.2-3mdk)] on linux2  4  Type "help", "copyright", "credits" or "license" for more information.  5  >>> 2 + 2  6  4  7  >>> 7 * 6  8  42  9  >>> 5 ** 3  10  125  11  >>> for n in range(5):  12  . print n * 9  13  .  14  0  15  9  16  18  17  27  18  36  19  >>> 

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

PyCrust устанавливает стандарт оболочки Python

Когда Вы работаете с Python в интерактивном режиме, Вы работаете в среде, называемой Python-оболочкой, которая подобна другим средам, типа окна DOS на платформах Microsoft или командной строке bash в Unix-системах.

Основа всех Python-оболочек, которую Вы видите, когда запускаете Python из командной строки, такая же, как и в листинге 4.1. Хотя Python и полезная оболочка, она полностью ориентирована на текстовый, а не графический режим, и не обеспечивает всех клавиатурных комбинаций (shortcuts) или всплывающих подсказок (hints), которые способен предоставить Python. Чтобы предоставить эти дополнительные функциональные возможности, были специально разработаны несколько графических Python-оболочек. Наиболее общеизвестный является интегрированная среда разработки IDLE (Integrated DeveLopment Environment), которая является стандартной частью дистрибутива Python. Оболочка IDLE, как показано на рисунке 4.1, выглядит подобно оболочке командной строки Python, но имеет дополнительные графические возможности, подобные подсказкам о вызове (calltips).

Другие инструменты для разработки на Python, типа PythonWin или Boa Constructor, имеют графическую оболочку Python, подобную той, что и в IDLE. Существование всех этих оболочек, подтолкнуло к созданию PyCrust. Хотя каждая из инструментальных оболочек имеет некоторые полезные возможности, например, повторный вызов команды, автозавершение и подсказки о вызове, нет инструмента, который бы имел полный набор всех этих возможностей. Одна из целей PyCrust состоит в том, чтобы обеспечить полный набор возможностей всех существующих оболочек Python.

Рисунок 4.1 Оболочка IDLE предоставляет для функций и методов подсказки о вызове

Другой причиной, по которой был создан PyCrust, было то, что инструментальные средства, написанные при помощи одной GUI-библиотеки, часто не работают с кодом от другой GUI-библиотеки. Например, IDLE написан с помощью Tkinter, а не wxPython. До недавнего времени, если бы Вы попытались импортировать и использовать модуль wxPython из Python-оболочки IDLE, Вы бы получили конфликт между циклом событий wxPython и циклом событий Tkinter, что привело бы к останову или краху программы.

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

Какие у PyCrust полезные возможности?

Теперь мы рассмотрим некоторые возможности, которые предоставляет оболочка PyCrust. PyCrust выглядит знакомым, т. к. отображает те же самые строки информации и использует те же сообщения, что и командная строка оболочки Python. Рисунок 4.2 показывает открытый экран PyCrust.

Вы заметите, что фрейм PyCrust, содержащий элемент wx.SplitterWindow, разделен на две секции: верхняя секция напоминает обычную оболочку Python, нижняя секция содержит элемент управления Notebook, который включает разнообразные ярлыки, показывающие информацию о текущем пространстве имен (namespace). Верхняя секция (PyCrust-оболочка), имеет несколько полезных возможностей, обсуждаемых в следующих нескольких подразделах.

Рисунок 4.2 Запущенный PyCrust показывает оболочку и интерфейс на основе ярлыков записной книжки

Автозавершение

Когда Вы вводите имя объекта, завершаемое оператором-точкой, происходит автозавершение. PyCrust показывает алфавитный список всех известных атрибутов данного объекта. Если Вы введете дополнительные буквы, подсвеченный выбор в списке изменится, и будет соответствовать введенным Вами буквам. Если нужный Вам элемент выделен, нажмите клавишу Tab и PyCrust заполнит за Вас остальную часть имени атрибута.

На рисунке 4.3 PyCrust показывает список атрибутов для объекта строки. Список автозавершения включает все свойства и методы этого объекта.

Рисунок 4.3 Отображение атрибутов объекта при автозавершении

Подсказки о вызове (Calltip) и параметры по умолчанию

Когда Вы вводите левую круглую скобку после имени вызываемого объекта, PyCrust показывает окно подсказки о вызове (calltip) (см. рисунок 4.4) с информацией о передаваемых при вызове аргументах и справочной информацией по этому вызову (если она имеется).

Вызываемый объект может быть функцией, методом, встроенной командой или классом. Все они могут принимать аргументы и могут иметь справочную информацию, предоставляющую сведения о том, что элемент делает и какой тип возвращаемого значения. Эта информация выводится во временном окне, выше или ниже знака каретки, что устраняет потребность обращаться к документации. Если Вы знаете, как использовать объект вызова, игнорируйте подсказку о вызове и продолжайте ввод.

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

Рисунок 4.4 Подсказка о вызове выводит информацию о вызываемом объекте

Выделение синтаксиса

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

Многие возможности PyCrust делают возможным поставляемое с wxPython мощное управление текстом. Компонент wx.stc.StyledTextCtrl — wxPython обертка на исходном коде компонента редактирования Scintilla, разработанная Нейлом Ходсоном (Neil Hodgson). Scintilla (www.scintilla.org) используется различными приложениями редактирования исходного кода, включая поставляемые с wxPython демонстрационные программы. Хотя и было бы не легко создать функциональность редактора исходного кода подобную оболочке wxPython, но создать PyCrust без Scintilla было бы практически невозможно.

Помощь по языку Python

PyCrust обеспечивает полную поддержку функций справочной системы по языку Python. Как показано на рисунке 4.5, функция помощи выводит информацию практически обо всех аспектах Python.

Рисунок 4.5 Использование внутри PyCrust функции помощи по Python

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

Повторный вызов команды

Есть много способов избежать ввода внутри PyCrust. Большинство из них включают в себя запоминание всего того, что Вы предварительно ввели, внесение необходимых изменений и отсылку результата интерпретатору Python. Например, PyCrust поддерживает историю всех команд, которые Вы ввели в текущем сеансе работы. Вы можете повторно вызвать из истории команд любую предварительно введенную команду Python (однострочную или многострочную). Таблица 4.1 показывает список клавиатурных комбинаций, которые касаются этих функциональных возможностей.

Таблица 4.1 Клавиатурные комбинации повторного вызова команд оболочки PyCrust

Как Вы видите, отдельные команды для восстановления и вставки старых команд различаются тем, как PyCrust обращается с текстом, введенным в текущем запросе wxPython. Для замены введенного Вами текста используйте одну из комбинаций, которая извлекает элемент истории. Используйте одну из комбинаций для вставки элемента истории, чтобы вставить старую команду в позицию каретки.

Вставка строки в середину многострочной команды работает по-другому, чем вставка в команду однострочной команды. Нажимая клавишу Enter, Вы не можете вставить строку в многострочную команду, потому что она посылает текущую команду интерпретатору Python. Вместо этого, чтобы вставить разрыв на текущей строке, нажмите Ctrl+Enter. Если Вы находитесь в конце строки, за текущей строкой вставляется пустая строка. Этот процесс похож на вырезание и вставку текста в обычном текстовом редакторе.

Заключительный метод повторного вызова команды состоит в том, чтобы просто переместить каретку на команду, которую Вы хотите вызвать и нажать Enter. PyCrust скопирует эту команду в текущую позицию ввода Python, и поместит каретку в конец. Вы сможете затем изменить команду или снова нажать Enter и послать команду интерпретатору.

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

Вырезание (Cut) и вставка (Paste)

Иногда Вам может потребоваться использовать разработанный в оболочке код без необходимости его повторного ввода. В других случаях, Вы можете найти образец кода, возможно в онлайновом руководстве и использовать его в Python-оболочке. PyCrust предоставляет несколько простых операций вырезания и вставки, перечисленных в таблице 4.2.

Таблица 4.2 Клавиатурные комбинации вырезания и вставки оболочки PyCrust

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

Иногда, когда Вы копируете код, Вам может понадобиться удалить приглашения PyCrust, как например, при копировании кода в ваши исходные файлы. В другой раз, Вы захотите сохранить приглашения, например, когда Вы копируете примеры в какой-либо документ или отправляете в группу новостей. PyCrust обеспечивает оба варианта при копировании текста из оболочки.

Стандартное окружение оболочки

В максимально возможной степени в пределах среды wxPython, PyCrust ведет себя так же, как и Python-оболочка командной строки. Имеются в виду некоторые необычные ситуации, как например, травление (pickling) экземпляров классов, которые определены в рамках сеанса оболочки. Единственная область, где PyCrust даёт сбой, кроется в его способности замещать функциональные возможности командной строки, являющиеся клавиатурными прерываниями. Как только код Python введен в PyCrust-оболочку, нет возможности прервать его выполнение. Например, предположим, Вы закодировали в PyCrust бесконечный цикл, как в следующем примере:

 1  >>> while True:  2  . print "Hello"  3  . 

После того, как Вы нажмете Enter, и код отправится интерпретатору Python, PyCrust приостановит ответную реакцию. Чтобы прерывать бесконечный цикл, завершите программу PyCrust. Этот недостаток PyCrust — отличие от Python-оболочки командной строки, которая сохраняет способность обрабатывать клавиатурное прерывание (Ctrl+C). В Python-оболочке командной строки Вы увидели бы следующее поведение:

 1  >>> while True:  2  . print "Hello"  3  .  4  Hello  5  Hello  6  Hello  7  Hello  8  Hello  9  Hello  10  Hello  11  Hello  12  Hello  13  Hello  14  Hello  15  Traceback (most recent call last):  16  File " ", line 2, in ?  17  KeyboardInterrupt  18  >>> 

Механизм обработки событий в GUI среде чрезвычайно затрудняет решение, которое позволило бы PyCrust преодолеть бесконечный цикл или прервать любую длительно исполняемую последовательность введенного в приглашение оболочки кода. Будущая версия PyCrust может обеспечить решение этого вопроса. Между тем, помните об этом поведении. К счастью, это единственное известное различие между PyCrust и стандартной командной оболочкой. Во всех других отношениях, оболочка PyCrust работает точно так же, как и оболочка командной строки Python.

Динамическое обновление

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

На рисунке 4.6 PyCrust показывает доступные для нового класса варианты автозавершения.

На рисунке 4.7 PyCrust показывает подсказку о вызове для недавно определенного метода класса.

Рисунок 4.6 Динамически генерированная PyCrust информация автозавершения

Рисунок 4.7 Динамически генерированная PyCrust информация подсказки о вызове

Это иллюстрирует реализуемый PyCrust способ динамического выполнения Python, который невозможен в других языках программирования со статическим вводом и компиляцией.

Каково назначение страниц PyCrust?

В нижней половине интерфейса PyCrust расположен элемент наподобие блокнота (notebook), который включает несколько страниц полезной информации. Появляющаяся при старте страница PyCrust – это Namespace (пространство имен).

Страница Namespace (Пространство имен)

Показанная на рисунке 4.8 страница Namespace, разделена на две части, где снова используется элемент wx.SplitterWindow. Левая сторона содержит элемент управления деревом, который отображает текущее пространство имен, а правая сторона отображает информацию о текущем объекте, выбранном в дереве пространства имен.

Рисунок 4.8 Дерево пространства имен PyCrust позволяет погружаться в объекты и инспектировать их атрибуты

Дерево Namespace представляет иерархический вид всех объектов в текущем пространстве имен. В нем содержатся пункты, которые возвращаются при работе встроенной функции Python locals(). На рисунке 4.8, мы импортировали пакет wx и выбрали его в дереве пространства имен. Правая сторона отображает имя выбранного элемента, его тип и текущее значение. Если объект имеет ассоциированный с ним исходный код, PyCrust также его отображает. В этом случае, wx является пакетом wxPython, поэтому PyCrust отображает исходный код из файла init .py, который находится в директории wx. Первая строка экрана предоставляет имя объекта, полностью пригодное, для операций вырезания и вставки в оболочку PyCrust или в исходный код Вашего приложения. Например, если Вы импортируете и откроете модуль locale, Вы можете достичь элементов этого модуля, сохраненных внутри атрибута словаря encoding_alias. Как только Вы выберете один из этих элементов, Вы можете вырезать и вставлять его отображаемое имя непосредственно в оболочку PyCrust, например следующим образом:

 1  >>> import locale  2  >>> locale.encoding_alias['en']  3  'ISO8859-1'  4  >>> 

В этом случае, PyCrust снабдил нас полностью пригодным именем (locale.encoding_alias[‘en’]), использующим индексную нотацию Python ([‘en’]) для ссылки на определенный пункт словаря encoding_alias. Этот механизм также работает и для списков. Если Вы что-то указываете в дереве пространства имен для ссылки в своем коде, PyCrust даёт Вам точный синтаксис для выполнения этой задачи.

Страница Display (Экран)

Страница Display отображает красивый вид объекта. PyCrust имеет встроенную функцию pp(), которая использует модуль качественной печати Python (pprint) для выполнения хорошо отформатированного вида любого объекта wxPython. Информация на странице Display корректируется при каждом обновлении отображаемого объекта, не требуя от Вас явного импортирования и многократного использования pprint.

Например, чтобы увидеть, как изменяется содержимое списка при манипулировании им в оболочке, Вы можете выделить страницу Display, использовать функцию pp() в пределах оболочки для отображения Вашего объекта списка на странице Display, а затем запустить код, который модифицирует этот список. Всякий раз, когда список будет изменяться, изменения сразу же будут появляться на странице Display.

Страница Calltip (Подсказка о вызове)

Страница Calltip отображает содержание последней подсказки о вызове Python-оболочки. Выберите страницу Calltip, если Вы работаете с вызываемыми объектами, которые требуют большого количества передаваемых им параметров. При использовании пакета wxPython существует много классов, которые могут иметь много методов, принимающих множество параметров. Например, для создания wx.Button, Вы можете передать вплоть до восьми параметров, один из которых обязателен, тогда как другие семь имеют значение по умолчанию. Страница Calltip отображает следующую информацию о конструкторе wx.Button:

 1  __init__(self, Window parent, int id=-1, String label=EmptyString,  2  Point pos=DefaultPosition, Size size=DefaultSize,  3  long style=0, Validator validator=DefaultValidator,  4  String name=ButtonNameStr) -> Button  5  Create and show a button. The preferred way to create standard buttons  6  is to use a standard ID and an empty label. In this case wxWigets will  7  automatically use a stock label that corresponds to the ID given. In  8  addition, the button will be decorated with stock icons under GTK+2. 

Поскольку классы wxPython в действительности надстроены над классами C++, информация для подсказок о вызове основывается только на документации класса (docstrings). Она сгенерирована, чтобы показать требуемые базовому классу C++ состав передаваемых параметров и их тип (int, string, point и т.д.). Вот почему подсказка о вызове конструктора wx.Button показывается таким способом.

PyCrust инспектирует объекты, полностью определенные на языке Python, полагаясь на характер их аргументов.

Страница Session (Сеанс)

Страница Session (в новых версиях – History (История)) является простым текстовым элементом, который перечисляет все команды, введенные в текущем сеансе оболочки. Это облегчает вырезание и вставку команд для использования где-нибудь еще, при этом нет необходимости удалять отклик, который возвращается интерпретатором wxPython.

Страница Dispatcher (Диспетчер)

PyCrust включает модуль, названный dispatcher, предоставляющий механизм для свободного связывания объектов внутри приложения. PyCrust использует диспетчер, чтобы держать аспекты своего интерфейса актуальными, когда команды посылаются из оболочки в интерпретатор Python. Страница Dispatcher (рисунок 4.9) содержит информацию о сигналах, проходящих через механизм диспетчеризации. Прежде всего это полезно при работе непосредственно с PyCrust.

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

 1  class DispatcherListing(wx.TextCtrl):  2  """Text control containing all dispatches for session."""  3  def __init__(self, parent=None, id=-1):  4  style = (wx.TE_MULTILINE | wx.TE_READONLY |  5  wx.TE_RICH2 | wx.TE_DONTWRAP)  6  wx.TextCtrl.__init__(self, parent, id, style=style)  7  dispatcher.connect(receiver=self.spy)  8  def spy(self, signal, sender):  9  """Receiver for Any signal from Any sender."""  10  text = '%r from %s' % (signal, sender)  11  self.SetInsertionPointEnd()  12  start, end = self.GetSelection()  13  if start != end:  14  self.SetSelection(0, 0)  15  self.AppendText(text + '\n') 

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

Рисунок 4.9 Диспетчер PyCrust информирует о том, каким образом команды передаются интерпретатору Python

Как связать PyCrust с моим wxPython-приложением?

Допустим, Вы использовали wxPython для создания программы, Ваша программа работает, и теперь Вы хотели бы лучше понять, как именно она работает. В этой главе Вы увидели возможности PyCrust и они могут оказаться очень полезными для понимания функционирования Вашей программы. Но Вам не хотелось бы изменять программу только для того, чтобы использовать ее с PyCrust. Что же делать в этом случае?

Передавая имя Вашей программы утилите PyWrap, Вы сможете без изменений ее запустить под оболочкой PyCrust. Листинг 4.2 показывает программу spare.py, которую мы подготовили для связи с PyCrust.

Листинг 4.2 Программа spare.py, подготовленная для связи с PyCrust

 1  #!/usr/bin/env python  2  """Spare.py is a starting point for simple wxPython programs."""  3  import wx  4    5  class Frame(wx.Frame):  6  pass  7  class App(wx.App):  8  def OnInit(self):  9  self.frame = Frame(parent=None, id=-1, title='Spare')  10  self.frame.Show()  11  self.SetTopWindow(self.frame)  12  return True  13  if __name__ == '__main__':  14  app = App()  15  app.MainLoop() 

Для запуска этой программы в окружении PyCrust, передайте имя программы в PyWrap из каталога, где находится spare.py. Командная строка под Linux выглядит так:

$ pywrap spare.py

Когда PyWrap стартует, он попытается импортировать модуль, включенный в командную строку. Затем PyWrap ищет в этом модуле подкласс wx.App и создает экземпляр этого класса. После этого создает окно PyWrap wx.py.crust.CrustFrame с оболочкой, открывает прикладной объект в дереве пространства имен PyCrust и запускает цикл событий wxPython.

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

Листинг 4.3 Исходный код PyWrap.py

 1  """PyWrap is a command line utility that runs a python  2  program with additional runtime tools, such as PyCrust."""  3  __author__ = "Patrick K. O'Brien "  4  __cvsid__ = "$Id: PyCrust.txt,v 1.15 2005/03/29 23:39:27 robind Exp $"  5  __revision__ = "$Revision: 1.15 $"[11:-2]  6  import os  7  import sys  8  import wx  9  from wx.py.crust import CrustFrame  10  def wrap(app):  11  wx.InitAllImageHandlers()  12  frame = CrustFrame()  13  frame.SetSize((750, 525))  14  frame.Show(True)  15  frame.shell.interp.locals['app'] = app  16  app.MainLoop()  17  def main(modulename=None):  18  sys.path.insert(0, os.curdir)  19  if not modulename:  20  if len(sys.argv) < 2:  21  print "Please specify a module name."  22  raise SystemExit  23  modulename = sys.argv[1]  24  if modulename.endswith('.py'):  25  modulename = modulename[:-3]  26  module = __import__(modulename)  27  # Find the App class.  28  App = None  29  d = module.__dict__  30  for item in d.keys():  31  try:  32  if issubclass(d[item], wx.App):  33  App = d[item]  34  except (NameError, TypeError):  35  pass  36  if App is None:  37  print "No App class was found."  38  raise SystemExit  39  app = App()  40  wrap(app)  41  if __name__ == '__main__':  42  main() 

После запуска команды PyWrap будут отображены как простой фрейм из spare.py, так и фрейм PyCrust.

PyCrust в действии

Теперь давайте посмотрим, что мы можем сделать с прикладным фреймом из оболочки PyCrust. Рисунок 4.10 отображает результат. Сначала мы spare.py импортируем wx и добавим в наш фрейм панель:

 1  >>> import wx  2  >>> app.frame.panel = wx.Panel(parent=app.frame)  3  >>> app.frame.panel.SetBackgroundColour('White')  4  True  5  >>> 

Рисунок 4.10 Использование PyWrap для оптимизации выполнения wxPython-программы

Добавленная к фрейму панель имеет по умолчанию серый цвет, затем он изменен на белый. Однако, установка цвета фона панели не проявляется немедленно. Для этого нужно инициировать событие, которое заставит панель перерисовать себя на основании свойства ее нового фонового цвета. Единственный способ инициировать это событие – потребовать, чтобы панель себя обновила:

 1  >>> app.frame.panel.Refresh() 

Теперь белая панель отображена, и мы на один шаг ближе к пониманию деталей того, как действительно работает wxPython.

Давайте добавим еще и строку состояния (status bar):

 1  >>> app.frame.statusbar = app.frame.CreateStatusBar(number=3)  2  >>> app.frame.statusbar.SetStatusText("Left", 0)  3  >>> app.frame.statusbar.SetStatusText("Center", 1)  4  >>> app.frame.statusbar.SetStatusText("Right", 2) 

Заметьте, как строка состояния появляется в пределах фрейма, не изменяя самые крайние размеры фрейма. Также, обратите внимание, что добавленный в каждую из трех секций текст строки состояния появляется немедленно и не требует обновления. Теперь давайте добавим меню и строку меню (menubar):

 1  >>> app.frame.menubar = wx.MenuBar()  2  >>> menu = wx.Menu()  3  >>> app.frame.menubar.Append(menu, "Primary")  4  True  5  >>> app.frame.SetMenuBar(app.frame.menubar)  6  >>> menu.Append(wx.NewId(), "One", "First menu item")  7  wx.core.MenuItem; proxy of C++ wxMenuItem instance at  8  _d8043d08_p_wxMenuItem>  9  >>> menu.Append(wx.NewId(), "Two", "Second menu item")  10  wx.core.MenuItem; proxy of C++ wxMenuItem instance at  11  _40a83e08_p_wxMenuItem>  12  >>> 

Когда Вы манипулируете своими wxPython-объектами в оболочке PyCrust, помните о влиянии этих изменений на Вашу выполняемую программу. Попытайтесь ответить на следующие вопросы. Когда меню действительно появляется в пределах фрейма? Какие атрибуты меню Вы можете изменить, пока выполняется программа? Можете ли Вы добавить дополнительные пункты меню? Можете ли Вы удалить их? Можете ли Вы их блокировать? Изучение всех этих возможностей должно помочь лучше понять wxPython и обеспечить Вас большей уверенностью, когда придет время записи фактического программного кода.

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

Что еще имеется в пакете Py?

Все программы PyCrust просто используют включенные в пакет Py модули Python, например, такие как shell.py, crust.py, introspect.py и interpreter.py. Эти программы являются строительными блоками, которые использованы для создания PyCrust и Вы можете использовать их раздельно или вместе.

Считайте, что PyCrust представляет одно направление трансляции битов и частей функционального наполнения, содержащегося в пакете Py. PyShell представляет другое направление, а PyAlaMode — третье. В каждом из этих случаев, большая часть основного кода является общей для всех них с некоторым отдаленным изменением контейнеров. Поэтому считайте пакет Py библиотекой модулей, которые Вы можете собрать, как Вам нравится, и где бы Вы хотели отобразить оболочку wxPython, в редакторе кода или в информации времени выполнения в пределах Вашей программы.

Внутри пакета Py существует четкое разделение между модулями, обеспечивающими функциональность интерфейса пользователя и другими модулями. Такое разделение значительно облегчает использование этих модулей в Ваших собственных программах. Все модули, начинающиеся с Py, например, PyCrust, PyShell, PyAlaMode и PyAlaCarte – это GUI программы конечного пользователя. У вас не будет необходимости импортировать в Ваши программы какой-нибудь из этих модулей. Следующий параграф описывает модули конечного пользователя.

Работа с GUI-программами

Программы пользовательского уровня является пакетами, которые поддерживают модули различными путями. Таблица 4.3 приводит описание программ пользовательского уровня.

Таблица 4.3 Программы конечного пользователя, включенные в пакет Py

Работа с модулями поддержки

Модули поддержки обеспечивает базовую функциональность программ конечного пользователя, и могут быть импортированы в Ваши собственные программы. Эти модули являются по существу строительными блоками для создания Py-программ пользовательского уровня. Таблица 4.4 содержит перечень модулей поддержки, являющихся частью пакета Py, вместе с их кратким описанием.

Таблица 4.4 Модули поддержки пакета Py

Модуль buffer

Модуль buffer содержит класс Buffer, который поддерживает обычное редактирование файла. Буфер имеет методы такие, как new(), open(), hasChanged(), save() и saveAs(). Обрабатываемый посредством буфера файл представлен экземпляром класса Document, определенного в модуле document. Фактическое редактирование содержимого файла происходит посредством одного или более экземпляров класса Editor, определенного в модуле editor. Буфер действует как посредник между одним или более редакторами и фактически представляет физический файл.

Уникальная характерная особенность класса Buffer состоит в том, что каждому экземпляру буфера назначается отдельный экземпляр интерпретатора Python. Эта возможность позволяет использовать буферы в приложениях, которым нужно предоставить функции автозавершения, подсказки о вызове и другие вспомогательные вещи стадии выполнения при редактировании файлов исходного кода Python. Каждый буфер интерпретатора полностью независим и обновляется при вызове метода буфера updateNamespace(). Листинг 4.4 приводит исходный код этого метода.

Листинг 4.4 Метод буфера для обновления пространства имен

 1  def updateNamespace(self):  2  """Update the namespace for autocompletion and calltips.  3  Return True if updated, False if there was an error."""  4  if not self.interp or not hasattr(self.editor, 'getText'):  5  return False  6  syspath = sys.path  7  sys.path = self.syspath  8  text = self.editor.getText()  9  text = text.replace('\r\n', '\n')  10  text = text.replace('\r', '\n')  11  name = self.modulename or self.name  12  module = imp.new_module(name)  13  newspace = module.__dict__.copy()  14  try:  15  try:  16  code = compile(text, name, 'exec')  17  except:  18  raise  19  try:  20  exec code in newspace  21  except:  22  raise  23  else:  24  # No problems, so update the namespace.  25  self.interp.locals.clear()  26  self.interp.locals.update(newspace)  27  return True  28  finally:  29  sys.path = syspath  30  for m in sys.modules.keys():  31  if m not in self.modules:  32  del sys.modules[m] 

Данный метод формирует текст в редакторе при помощи встроенного метода Python compile, а затем выполняет его, используя ключевое слово exec. Если компиляция успешна, результат устанавливает количество переменных в пространстве имен newspace. В результате выполнения, при обновлении локального пространства имен интерпретатора, обеспечивается доступ интерпретатора к любым определенным в буфере редактора классам, методам или переменным.

Модуль crust

Модуль crust содержит шесть характерных для программного приложения PyCrust элементов GUI. Основной класс CrustFrame является подклассом wx.Frame. Если Вы просмотрите листинг 4.1, то можете увидеть, как программа PyWrap импортирует CrustFrame и создает его экземпляр. Это самый простой способ вставить фрейм PyCrust в Вашу собственную программу. Если Вы хотите что-то меньшее, чем целый фрейм, можете использовать один или несколько классов, указанных в таблице 4.5.

Таблица 4.5 Классы, определенные в модуле crust

Эти GUI-элементы интерфейса могут быть использованы в любой программе wxPython и обеспечивают полезными интроспективными визуальными возможностями.

Модуль dispatcher

Диспетчер обеспечивает услуги глобальной обработки сигналов. Это означает, что он действует как посредник, разрешая объектам посылать и получать сообщения, без необходимости что-либо знать друг о друге. Все что им нужно знать — это посылаемый сигнал (обычно простая строка). Один или несколько объектов могут запросить, чтобы диспетчер уведомлял их всякий раз, когда сигнал посылается, и один или несколько объектов могут указать диспетчеру послать некоторый конкретный сигнал.

Листинг 4.5 иллюстрирует, почему диспетчер такой полезный. Поскольку все Py-программы построены на одних и тех же основных модулях, оба, и PyCrust и PyShell используют почти идентичный код. Единственное различие в том, что PyCrust включает блокнотный элемент с дополнительными функциями, подобно дереву пространства имен, обновляемый всякий раз, когда интерпретатору посылаются команды. Интерпретатор использует диспетчера для посылки сообщения (сигнала) всякий раз, когда через него продвигается команда:

 1  def push(self, command):  2  """Send command to the interpreter to be executed.  3  Because this may be called recursively, we append a new list  4  onto the commandBuffer list and then append commands into  5  that. If the passed in command is part of a multi-line  6  command we keep appending the pieces to the last list in  7  commandBuffer until we have a complete command. If not, we  8  delete that last list."""  9  command = str(command) # In case the command is unicode.  10  if not self.more:  11  try: del self.commandBuffer[-1]  12  except IndexError: pass  13  if not self.more: self.commandBuffer.append([])  14  self.commandBuffer[-1].append(command)  15  source = '\n'.join(self.commandBuffer[-1])  16  more = self.more = self.runsource(source)  17  dispatcher.send(signal='Interpreter.push', sender=self,  18  command=command, more=more, source=source)  19  return more 

Различные задействованные части модулей crust и filling самостоятельно настраиваются на получение этого сигнала, связываясь с диспетчером в своих конструкторах. Листинг 4.6 содержит полный исходный код элемента SessionListing, размещенного в PyCrust на странице Session:

Листинг 4.6 Код сеансовой страницы PyCrust

 1  class SessionListing(wx.TextCtrl):  2  """Text control containing all commands for session."""  3  def __init__(self, parent=None, id=-1):  4  style = (wx.TE_MULTILINE | wx.TE_READONLY |  5  wx.TE_RICH2 | wx.TE_DONTWRAP)  6  wx.TextCtrl.__init__(self, parent, id, style=style)  7  dispatcher.connect(receiver=self.push,  8  signal='Interpreter.push')  9  def push(self, command, more):  10  """Receiver for Interpreter.push signal."""  11  if command and not more:  12  self.SetInsertionPointEnd()  13  start, end = self.GetSelection()  14  if start != end:  15  self.SetSelection(0, 0)  16  self.AppendText(command + '\n') 

Обратите внимание, как получатель SessionListing (его метод push()) игнорирует направленные интерпретатором параметры sender и source. Диспетчер в этом смысле очень гибкий и только посылает принимаемые получателями параметры.

Модуль editor (редактор)

Модуль editor содержит все компоненты редактирования GUI, которые используются в программах PyAlaCarte и PyAlaMode. Если Вам необходимо включить в свою программу редактор исходного кода Python, используйте классы, описанные в таблице 4.6.

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

Таблица 4.6 Классы, определенные в модуле editor

Модуль filling (инспектирование)

Модуль filling содержит все элементы управления GUI, которые позволяют пользователю передвигаться по пространству имен объектов и отображать информацию времени выполнения этих объектов. Четыре определенных в модуле filling класса описаны в таблице 4.7.

Таблица 4.7 Классы, определенные в модуле filling

Использование этих классов в Вашей программе позволит легко создать иерархическое дерево пространства имен Python. Оно может быть использовано в качестве быстрого обозревателя данных, если Вы настраиваете эти данные, как объекты Python.

Модуль interpreter (интерпретатор)

Определенный в классе Interpreter модуль interpreter основан на классе InteractiveInterpreter модуля code из стандартной библиотеки Python. Кроме передачи исходного кода в Python, класс Interpreter также отвечает за обеспечение списков автозавершения, информации подсказок о вызове и даже за обработку кодов клавиш, инициирующих автозавершение (обычно это код клавиши точки «.»).

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

Модуль introspect

Модуль introspect используется классами Interpreter и FillingTree. Он обеспечивает ряд функций поддержки интроспективного типа для подсказок о вызове и команд автозавершения. Далее показано использование wx.py.introspect для получения всех названий атрибутов объекта списка, исключая атрибуты с ведущими двойными подчеркиваниями:

 1  >>> import wx  2  >>> L = [1, 2, 3]  3  >>> wx.py.introspect.getAttributeNames(L, includeDouble=False)  4  ['append', 'count', 'extend', 'index', 'insert', 'pop',  5  'remove', 'reverse', 'sort']  6  >>> 

Функция !getAttributeNames() используется классом FillingTree для инспектирования иерархии пространства имен. Один из лучших способов понять модуль introspect состоит в том, чтобы взглянуть на тесты устройства, которые он успешно проходит. Просмотрите файл test_introspect.py в директории установки Python Lib/site-packages/wx/py/tests.

Модуль shell

Модуль shell содержит GUI элементы, которые определяют интерфейс оболочки Python внутри программ PyCrust, PyShell и PyAlaMode. Таблица 4.8 приводит описание каждого элемента. Основным классом является ShellFrame, подклассом — frame.Frame. Он содержит экземпляр класса Shell, выполняющий основной объем работы по обеспечению диалоговой среды Python.

Таблица 4.8 Классы, определенные в модуле shell

Класс ShellFacade был создан при разработке оболочки PyCrust для упрощения доступа из нее к объекту оболочки. Когда Вы запускаете PyCrust или PyShell, экземпляр класса Shell становится доступным в оболочке Python. Например, следующим образом Вы можете вызвать в приглашении оболочки метод оболочки about():

 1  >>> shell.about()  2  Author: "Patrick K. O'Brien "  3  Py Version: 0.9.4  4  Py Shell Revision: 1.7  5  Py Interpreter Revision: 1.5  6  Python Version: 2.3.3  7  wxPython Version: 2.4.1.0p7  8  Platform: linux2  9  >>> 

Поскольку оболочка Shell наследует StyledTextCtrl, она содержит свыше 600 атрибутов. Большинство атрибутов используются в приглашении оболочки, так что ShellFacade был создан, чтобы ограничивать количество атрибутов, появляющихся в списке автозавершения при вводе на запрос оболочки. Теперь объект оболочки отображает только около 25 наиболее полезных атрибутов оболочки. Если Вы хотите использовать некоторый атрибут, который не включен в список автозавершения, можете его ввести, и он будет направлен обычной оболочке, сохраненной в качестве атрибута внешнего вида.

Как использовать модули пакета Py в wxPython-программах?

Что делать, если в Вашем приложении Вам не нужен целый фрейм PyCrust? Что, если Вам просто нужен интерфейс оболочки в одном фрейме, и возможно вьювер пространства имён в другом? И что, если Вы хотите, чтобы они были постоянными дополнениями Вашей программы? Такие альтернативы не только возможны, они также довольно легко достигаются. Мы закончим эту главу примером того, как это может быть сделано. Мы вновь вернемся к созданной нами в главе 2 программе, которая имела строку меню, панель инструментов и строку состояния. Мы добавим другое меню с одним пунктом для отображения фрейма оболочки и другим пунктом для отображения фрейма инспектора объектов (filling frame). И, наконец, мы установим корень дерева объектов на объект фрейма из нашей основной программы. Результат показан на рисунке 4.11.

Рисунок 4.11 Базовая программа с фреймами оболочки и инспектирования

Листинг 4.7 показывает модифицированный исходный код (Для объяснения первоначальной программы обратитесь к главе 2). Как видите, чтобы добавить возможность запускать фрейм оболочки и фрейм инспектора объектов с каждым функционирующим фреймом основного приложения, использована всего пара дополнительных строк кода.

Листинг 4.7 Базовая программа с дополнительными работающими инструментами

 1  #!/usr/bin/env python  2  import wx   3  from wx.py.shell import ShellFrame (1) Импортирование фреймовых классов  4  from wx.py.filling import FillingFrame  5  import images  6  class ToolbarFrame(wx.Frame):  7  def __init__(self, parent, id):  8  wx.Frame.__init__(self, parent, id, 'Toolbars',  9  size=(300, 200))  10  panel = wx.Panel(self, -1)  11  panel.SetBackgroundColour('White')  12  statusBar = self.CreateStatusBar()  13  toolbar = self.CreateToolBar()  14  toolbar.AddSimpleTool(wx.NewId(), images.getNewBitmap(),  15  "New", "Long help for 'New'")  16  toolbar.Realize()  17  menuBar = wx.MenuBar()  18  menu1 = wx.Menu()  19  menuBar.Append(menu1, "&File")  20  menu2 = wx.Menu()  21  menu2.Append(wx.NewId(), "&Copy", "Copy in status bar")  22  menu2.Append(wx.NewId(), "C&ut", "")  23  menu2.Append(wx.NewId(), "Paste", "")  24  menu2.AppendSeparator()  25  menu2.Append(wx.NewId(), "&Options. ", "Display Options")  26  menuBar.Append(menu2, "&Edit")  27    28  menu3 = wx.Menu()  29  shell = menu3.Append(-1, "&wxPython shell", (2) Создание меню Debug и его элементов  30  "Open wxPython shell frame")  31  filling = menu3.Append(-1, "&Namespace viewer",  32  "Open namespace viewer frame")  33  menuBar.Append(menu3, "&Debug")  34  self.Bind(wx.EVT_MENU, self.OnShell, shell) (3) Установка обработчиков событий меню  35  self.Bind(wx.EVT_MENU, self.OnFilling, filling)  36    37  self.SetMenuBar(menuBar)  38    39    40  def OnCloseMe(self, event):  41  self.Close(True)  42    43  def OnCloseWindow(self, event):  44  self.Destroy()  45    46  def OnShell(self, event): (4) Обработчик пункта меню OnShell   47  frame = ShellFrame(parent=self)  48  frame.Show()  49    50  def OnFilling(self, event): (5) Обработчик пункта меню OnFilling   51  frame = FillingFrame(parent=self)  52  frame.Show()  53    54  if __name__ == '__main__':  55  app = wx.PySimpleApp()  56  app.frame = ToolbarFrame(parent=None, id=-1)  57  app.frame.Show()  58  app.MainLoop() 

(1) Здесь мы импортируем классы ShellFrame и FillingFrame.

(2) Мы добавляем пункты в наше третье меню Debug (Отладка) так же, как и в предыдущих двух меню и добавляем его в строку меню фрейма.

(3) Связывание функции с wx.EVT_MENU() позволяет нам ассоциировать с пунктом меню обработчик, вызываемый при выборе этого пункта меню.

(4) Когда пользователь выбирает оболочку Python в меню Debug, создается фрейм оболочки, наследуемый от фрейма панели инструментов (toolbar). Когда фрейм инструментальной панели закрывается, также закрываются любые открытые фреймы оболочки и инспектора объектов.

Резюме

  • Пакеты разработчика подобные wxPython по своей природе очень велики и сложны. Взаимодействие между GUI-элементами не всегда интуитивно, ход этого процесса определен событиями и ответами на события, а не линейной последовательностью выполнения. Использование инструментальных средств подобных оболочке PyCrust может существенно повысить Ваше понимание этой событийно-управляемой среды.
  • PyCrust — просто другая оболочка Python, подобная оболочкам, включенным в IDLE, Boa Constructor, PythonWin и другие инструментальные средства разработки. Тем не менее, PyCrust был создан с помощью wxPython, применение которого оправдывается, когда Вы разрабатываете программы на wxPython. В частности, у Вас не будет проблем с конфликтами событийных циклов, и Вы сможете манипулировать всеми аспектами Вашей программы при ее выполнении в рамках оболочки PyCrust и обозревателя пространства имен.
  • Поскольку PyCrust — часть дистрибутива wxPython, он устанавливается вместе с wxPython, включая весь исходный код. Это облегчает использование PyCrust, нивелируя трудности изучения и обеспечивая интроспективную функциональность Ваших программ.
  • Кроме того, модульный проект пакета Py облегчает выбор необходимых для Вашей программы модулей, например, для редактирования исходных текстов, просмотра пространства имен или для обеспечения функциональности оболочки.
  • PyCrust помогает Вам контролировать ключевые моменты поведения Вашей программы при ее выполнении. В следующей главе мы используем накопленные знания о wxPython, а также получим некоторую практическую помощь в том, как структурировать Ваши GUI-программы и при этом не запутаться.

Перевод: Савицкий Юрий

Книги/WxPythonInAction/Упрощение работы в wxPython при помощи PyCrust (последним исправлял пользователь alafin 2010-05-30 07:32:08)

  • MoinMoin Powered
  • Python Powered
  • GPL licensed
  • Valid HTML 4.01

PyCrust

wxPyWiki

PyCrust is an interactive Python shell, part of the Py collection of programs.

The Py Manual has documentation for PyCrust, PyAlaMode, PyAlaCarte, PyFilling, PyShell, and PyWrap:

PyCrust (последним исправлял пользователь localhost 2008-03-11 10:50:29)

  • Неизменяемая страница
  • Комментарии
  • Информация
  • Прикреплённые файлы
  • MoinMoin Powered
  • Python Powered
  • GPL licensed
  • Valid HTML 4.01

Управление персистентностью Python

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

Что такое персистентность?

Идея, лежащая в основе персистентности, довольно проста. Предположим, что у вас есть приложение Python для управления списком задач на каждый день, и вы хотите запоминать объекты приложения (свои отдельные задачи) между использованием этой программы. Другими словами, вы желаете сохранять свои объекты на жестком диске, а затем их извлекать. Это и есть персистентность. Чтобы выполнить эту задачу, у вас есть несколько возможностей, каждая из них обладает своими достоинствами и недостатками.

Например, вы могли бы хранить данные своего объекта в какой-нибудь разновидности форматированного текстового файла, как CSV-файл. Или вы могли бы использовать реляционную базу данных, такую как Gadfly, MySQL, PostgreSQL или DB2. Эти файловые форматы хорошо определены, а Python обладает надежными интерфейсами для всех этих механизмов хранения.

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

Тем самым, для некоторых приложений применение реляционных баз данных, возможно, не является идеальным. В частности, из-за того, что реляционные базы данных не понимают объекты. Наоборот, они навязывают свои собственные системы типов и свои собственные модели данных для отношений (таблиц), каждая из которых содержит набор записей (рядов), состоящих из фиксированного числа статически типизированных полей (столбцов). Если объектная модель для вашего приложения не преобразовывается легко в реляционную модель, у вас будут определенные сложности при преобразовании своего объекта в записи и обратно. Эту проблему часто именуют несогласованностью интерфейсов (impedence-mismatch problem).

Персистентность объектов

Если вы хотите прозрачно сохранять объекты Python, не теряя их тождественность (identity), тип и так далее, вам потребуется некоторая форма сериализации — процесса, который превращает произвольно сложные объекты в текстовое или бинарное представление этих объектов. Таким же образом вы должны быть в состоянии восстановить эту сериализованную форму объекта обратно в объект, такой же, как и оригинал. На Python процесс сериализации называется консервированием (pickling), и вы можете законсервировать/восстановить свои объекты в/из строки, файла на диске или любого объекта, подобного файлу. Ниже мы детально рассмотрим консервирование.

Предположим, что вам нравится идея хранить все как объект, избегая накладных расходов на преобразование объектов в какую-либо разновидность хранения, не основанную на объектах. Файлы консервированных объектов (pickle files) дают такую возможность, но иногда вам потребуется что-нибудь более надежное и расширяемое, чем просто эти файлы. Например, само по себе консервирование не решает проблему наименования и обнаружения файлов консервированных объектов, как и не поддерживает одновременный доступ к перманентным объектам (persistent objects). За такими свойствами обратитесь к чему-нибудь вроде ZOBD, объектной базе данных Z для Python. ZOBD — это надежная многопользовательская объектно-ориентированная система баз данных, способная хранить и управлять произвольно сложными объектами Python, включая поддержку транзакций и управление параллельным доступом (чтобы скачать ZOBD, см. Ресурсы). Весьма интересно, что даже ZOBD полагается на Питоновские встроенные возможности сериализации, и, чтобы эффективно использовать ZOBD, у вас должно быть полное понимание консервирования.

Другой интересный подход к решению проблемы персистентности, первоначально реализованный на Java, называется Prevayler (ссылку на статью о Prevayler, опубликованную на developerWorks, см. в Ресурсах). Недавно группа программистов Python перенесла Prevayler на Python, и итог их трудов под названием PyPerSyst находится на SourceForge (ссылка на этот проект приведена в Ресурсах). Концепция Prevayler/PyPerSyst также строится на встроенных возможностях сериализации языков Java и Python. PyPerSyst держит все систему объектов в памяти и обеспечивает восстановление системы после аварии, время от времени консервируя на диск мгновенное состояние (snapshot) системы и поддерживая лог команд, которые могут повторно применяться к последнему снимку. Но, несмотря на то, что приложения, использующие PyPerSyst, по этой причине ограничены доступной памятью (RAM), преимущество состоит в том, что полностью загруженная в память система объектов, «родных» для языка, является чрезвычайно быстродействующей, и ее гораздо легче реализовать, чем ту, которая, подобно ZOBD, разрешает больше объектов, чем можно одновременно держать в памяти.

После того, как мы кратко коснулись различных способов хранения перманентных объектов, давайте детально рассмотрим процесс консервирования. Хотя основной интерес состоит в нахождении путей поддержания объектов Python без обязательного преобразования их в какой-нибудь другой формат, многие проблемы по-прежнему остались неразрешенными: как эффективно законсервировать и восстановить как простые, так и сложные объекты, включая экземпляры классов, определенных пользователем (custom classes); как поддерживать ссылки на объекты, в том числе циклические и рекурсивные; и как управлять изменениями описания класса, не создавая проблем с ранее законсервированными экземплярами. Все эти вопросы будут освещены ниже при рассмотрении Питоновских возможностей сериализации.

Суп из консервированного Python

Поддержка Питоновского консервирования проистекает из модуля pickle и его «собрата» сPickle . Второй модуль был написан на C, чтобы обеспечить лучшую производительность, и рекомендован для большинства приложений. Мы продолжим обсуждение pickle , но на самом деле в примерах будет использоваться сPickle . Поскольку большинство примеров будет показано из оболочки Python, давайте начнем с демонстрации того, как импортировать сPickle , сохраняя возможность ссылаться на него как pickle :

>>> import cPickle as pickle

После того, как мы импортировали этот модуль, давайте посмотрим на интерфейс модуля pickle . Модуль pickle предоставляет следующие пары функций: dumps(object) возвращает строку, содержащую объект в формате консервирования; loads(string) возвращает объект, находящийся в строке консервирования; dump(object, file) записывает объект в файл, который может быть как действительно физическим файлом, так и любым подобным файлу объектом, имеющим метод write() , который принимает один строчный аргумент; load(file) возвращает объект, содержащийся в файле консервированного объекта.

По умолчанию dumps() и dump() создают консервированные объекты, используя печатаемое представление ASCII. Обе функции имеют конечный факультативный аргумент, который, если True , устанавливает, что консервированные объекты будут создаваться с использованием более быстрого и меньшего по размеру бинарного представления. Функции loads() и load() автоматически определяют, находится ли консервированный объект в бинарном или текстовом формате.

Листинг 1 иллюстрирует интерактивную сессию с использованием описанных функций dumps() и loads() :

Листинг 1. Иллюстрация dumps()и loads()

 Welcome To PyCrust 0.7.2 - The Flakiest Python Shell Sponsored by Orbtech - Your source for Python programming expertise. Python 2.2.1 (#1, Aug 27 2002, 10:22:32) [GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux-i386 Type "copyright", "credits" or "license" for more information. >>> import cPickle as pickle >>> t1 = ('this is a string', 42, [1, 2, 3], None) >>> t1 ('this is a string', 42, [1, 2, 3], None) >>> p1 = pickle.dumps(t1) >>> p1 "(S'this is a string'\nI42\n(lp1\nI1\naI2\naI3\naNtp2\n." >>> print p1 (S'this is a string' I42 (lp1 I1 aI2 aI3 aNtp2 . >>> t2 = pickle.loads(p1) >>> t2 ('this is a string', 42, [1, 2, 3], None) >>> p2 = pickle.dumps(t1, True) >>> p2 '(U\x10this is a stringK*]q\x01(K\x01K\x02K\x03eNtq\x02.' >>> t3 = pickle.loads(p2) >>> t3 ('this is a string', 42, [1, 2, 3], None) 

Заметьте, что расшифровать текстовый формат консервированного объекта (text pickle format) не слишком сложно. Действительно, все задействованные условные обозначения документированы в модуле pickle . Также следует отметить, что для простых объектов, использующихся в нашем примере, использование бинарного формата консервированного объекта (binary pickle format) не привело к большому выигрышу в размере. Однако, в реальной системе со сложными объектами, при использовании бинарного формата вы получите заметное улучшение в размере и скорости.

Далее мы рассмотрим некоторые примеры, в которых dump() и load() используются для работы с файлами и объектами, подобными файлам. Эти функции действуют во многом аналогично dumps() и loads() , с которыми мы только что познакомились — с одной дополнительной возможностью — функция dump() позволяет выгружать несколько объектов один за другим в один и тот же файл. Последующие вызовы load() будут извлекать эти объекты в том же самом порядке. Листинг 2 демонстрирует эту возможность в действии:

Листинг 2. Пример dump() и load()

 >>> a1 = 'apple' >>> b1 = >>> c1 = ['fee', 'fie', 'foe', 'fum'] >>> f1 = file('temp.pkl', 'wb') >>> pickle.dump(a1, f1, True) >>> pickle.dump(b1, f1, True) >>> pickle.dump(c1, f1, True) >>> f1.close() >>> f2 = file('temp.pkl', 'rb') >>> a2 = pickle.load(f2) >>> a2 'apple' >>> b2 = pickle.load(f2) >>> b2 >>> c2 = pickle.load(f2) >>> c2 ['fee', 'fie', 'foe', 'fum'] >>> f2.close() 

Мощь консервированных объектов

До сих пор освещались основы консервирования. В этом разделе мы рассмотрим некоторые нетривиальные проблемы, которые возникают при консервировании сложных объектов, включая экземпляры классов, определяемых пользователем. К счастью, Python, как вы увидите, довольно легко расправляется с такими задачами.

Переносимость

Консервированные объекты переносимы через пространство и время. Другими словами, формат файла консервированного объекта не зависит от архитектуры машины, что означает, что вы можете создать консервированный объект, например, под Linux и отправить его в программу Python, исполняемую под Windows и Mac OS. А если вы перейдете на более новую версию Python, вам не нужно беспокоиться о том, что вы, возможно, потеряете существующие консервированные объекты. Разработчики Python предусмотрели, что формат консервированного объекта будет совместим с более ранними версиями Python. К тому же подробная информация о текущем и поддерживаемом форматах предоставляется с модулем pickle :

Листинг 3. Получение информации о поддерживаемых форматах

 >>> pickle.format_version '1.3' >>> pickle.compatible_formats ['1.0', '1.1', '1.2'] 
Многочисленные ссылки, один и тот же объект

Переменная на Python- это ссылка на объект. Вы можете иметь многочисленные переменные, ссылающиеся на один и тот же объект. Оказывается, что Python не испытывает никаких сложностей при поддержании этого поведения и с консервированными объектами, как следует из Листинга 4:

Листинг 4. Поддержание объектных ссылок

 >>> a = [1, 2, 3] >>> b = a >>> a [1, 2, 3] >>> b [1, 2, 3] >>> a.append(4) >>> a [1, 2, 3, 4] >>> b [1, 2, 3, 4] >>> c = pickle.dumps((a, b)) >>> d, e = pickle.loads(c) >>> d [1, 2, 3, 4] >>> e [1, 2, 3, 4] >>> d.append(5) >>> d [1, 2, 3, 4, 5] >>> e [1, 2, 3, 4, 5] 
Циклические и рекурсивные ссылки

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

Листинг 5. Рекурсивная ссылка

 >>> l = [1, 2, 3] >>> l.append(l) >>> l [1, 2, 3, [. ]] >>> l[3] [1, 2, 3, [. ]] >>> l[3][3] [1, 2, 3, [. ]] >>> p = pickle.dumps(l) >>> l2 = pickle.loads(p) >>> l2 [1, 2, 3, [. ]] >>> l2[3] [1, 2, 3, [. ]] >>> l2[3][3] [1, 2, 3, [. ]] 

А теперь изучим циклическую ссылку:

Листинг 6. Циклическая ссылка

 >>> a = [1, 2] >>> b = [3, 4] >>> a.append(b) >>> a [1, 2, [3, 4]] >>> b.append(a) >>> a [1, 2, [3, 4, [. ]]] >>> b [3, 4, [1, 2, [. ]]] >>> a[2] [3, 4, [1, 2, [. ]]] >>> b[2] [1, 2, [3, 4, [. ]]] >>> a[2] is b 1 >>> b[2] is a 1 >>> f = file('temp.pkl', 'w') >>> pickle.dump((a, b), f) >>> f.close() >>> f = file('temp.pkl', 'r') >>> c, d = pickle.load(f) >>> f.close() >>> c [1, 2, [3, 4, [. ]]] >>> d [3, 4, [1, 2, [. ]]] >>> c[2] [3, 4, [1, 2, [. ]]] >>> d[2] [1, 2, [3, 4, [. ]]] >>> c[2] is d 1 >>> d[2] is c 1 

Заметьте, что мы получаем хоть и неприметно, но существенно различные результаты, если мы консервируем каждый объект отдельно, а не совместно внутри записи, как показано в Листинге 7:

Листинг 7. Консервирование по отдельности по сравнению с совместным консервированием внутри записи

 >>> f = file('temp.pkl', 'w') >>> pickle.dump(a, f) >>> pickle.dump(b, f) >>> f.close() >>> f = file('temp.pkl', 'r') >>> c = pickle.load(f) >>> d = pickle.load(f) >>> f.close() >>> c [1, 2, [3, 4, [. ]]] >>> d [3, 4, [1, 2, [. ]]] >>> c[2] [3, 4, [1, 2, [. ]]] >>> d[2] [1, 2, [3, 4, [. ]]] >>> c[2] is d 0 >>> d[2] is c 0 
Эквивалентны, но не всегда идентичны

С помощью последнего примера мы дали понять, что объекты идентичны, только если они ссылаются на один и тот же объект в памяти. В случае консервированных объектов, каждый из них восстанавливается в объект, который эквивалентен оригиналу, но не идентичен. Другими словами, каждый консервированный объект — это копия оригинального объекта.

Листинг 8. Восстановленные объекты как копии оригиналов

 >>> j = [1, 2, 3] >>> k = j >>> k is j 1 >>> x = pickle.dumps(k) >>> y = pickle.loads(x) >>> y [1, 2, 3] >>> y == k 1 >>> y is k 0 >>> y is j 0 >>> k is j 1 

В то же время мы видели, что Python способен поддерживать ссылки между объектами, которые консервируются как блок (unit). Однако, мы также узнали, что раздельные вызовы dump() лишают Python способности поддерживать ссылки на объекты вне консервируемого блока. Python, наоборот, создает копию объекта, на который ссылаются, и хранит его с консервируемым элементом (item). Для приложения, которое консервирует и восстанавливает иерархию одиночного объекта, это не является проблемой. Но это то, о чем не стоит забывать в других ситуациях.

Также нужно отметить возможность, позволяющую консервированным объектам поддерживать ссылки друг на друга, если все они законсервированы в один и тот же файл. Модули pickle и cPickle предоставляют объекты Pickler (и соответствующий Unpickler ), которые могут отслеживать (track) объекты, которые уже были законсервированы. При использовании Pickler , разделяемые и циклические ссылки будут законсервированы по ссылке, а не по значению:

Листинг 9. Поддержание ссылок среди отдельно консервированных объектов

 >>> f = file('temp.pkl', 'w') >>> pickler = pickle.Pickler(f) >>> pickler.dump(a) >>> pickler.dump(b) >>> f.close() >>> f = file('temp.pkl', 'r') >>> unpickler = pickle.Unpickler(f) >>> c = unpickler.load() >>> d = unpickler.load() >>> c[2] [3, 4, [1, 2, [. ]]] >>> d[2] [1, 2, [3, 4, [. ]]] >>> c[2] is d 1 >>> d[2] is c 1 
Неконсервируемые объекты

Некоторые типы объектов не могут быть законсервированы. Например, Python не может законсервировать файловый объект (или любой объект с ссылкой на файловый объект), потому что Python не может гарантировать, что он воссоздаст состояние файла при восстановлении. (Другие примеры настолько незначительны, что в данной статье о них не стоит упоминать.) Попытка законсервировать файловый объект приведет к следующей ошибке:

Листинг 10, Результат попытки законсервировать файловый объект

 >>> f = file('temp.pkl', 'w') >>> p = pickle.dumps(f) Traceback (most recent call last): File "", line 1, in ? File "/usr/lib/python2.2/copy_reg.py", line 57, in _reduce raise TypeError, "can't pickle %s objects" % base.__name__ TypeError: can't pickle file objects 
Экземпляры класса

Консервирование экземпляров класса требует немного больше внимания, чем консервирование типов простых объектов. Основная причина заключается в том, что Python консервирует данные экземпляра (обычно атрибут __dict__ ) и имя класса, но не код для класса. Когда Python восстанавливает экземпляр класса, он пытается импортировать модуль, содержащий описание класса, используя точные имена класса и модуля (включая любые префиксы пути к пакету) такими, какими они были во время консервирования экземпляра. Также заметьте, что описания класса должны располагаться на верхнем уровне модуля, что означает, что они не могут быть вложенными классами (классами, объявленными внутри других классов или функций).

Когда экземпляры класса восстанавливаются, обычно их метод __init__() не вызывается заново. Наоборот, Python создает родовой экземпляр класса, использует атрибуты экземпляра, которые были законсервированы, и устанавливает атрибут экземпляра __class__ так, чтобы он указывал на оригинальный класс.

Классы нового стиля (new-style class), появившиеся в Python 2.2, опираются на слегка отличный механизм восстановления. Несмотря на то, что результат этого процесса по существу такой же, как и с классами старого стиля, Python использует функцию _reconstructor() модуля copy_reg , чтобы восстановить экземпляры классов нового стиля.

Если вы хотите изменить поведение консервирования по умолчанию для экземпляров класса нового или старого стиля, вы можете описать специальные методы класса, а именно: __getstate__() и __setstate__ () — которые будут вызываться Python во время сохранения и восстановления информации о состоянии для экземпляров этого класса. В следующих разделах будут приведены некоторые примеры использования этих специальных методов.

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

Листинг 11. Описание класса нового стиля

 class Foo(object): def __init__(self, value): self.value = value 

Теперь мы можем законсервировать экземпляр Foo и изучить его представление:

Листинг 12. Консервирование экземпляра Foo

 >>> import cPickle as pickle >>> from Orbtech.examples.persist import Foo >>> foo = Foo('What is a Foo?') >>> p = pickle.dumps(foo) >>> print p ccopy_reg _reconstructor p1 (cOrbtech.examples.persist Foo p2 c__builtin__ object p3 NtRp4 (dp5 S'value' p6 S'What is a Foo?' sb. >>> 

Как мы видим, и имя класса, Foo , и полностью квалифицированное имя модуля, Orbtech.examples.persist , хранятся в консервированном объекте. Если бы мы законсервировали этот экземпляр в файл и восстановили бы его позже (или на другой машине), Python попытался бы импортировать модуль Orbtech.examples.persist , и если бы не смог это сделать, возбудил бы исключение. Подобные ошибки случились бы, если бы мы переименовали класс, модуль или переместили модуль в другой каталог.

Ниже приведена ошибка, которую выдаст Python, если мы переименуем класс Foo , а затем попытаемся загрузить ранее законсервированный экземпляр Foo :

Листинг 13. Попытка загрузить законсервированный экземпляр переименованного класса Foo

 >>> import cPickle as pickle >>> f = file('temp.pkl', 'r') >>> foo = pickle.load(f) Traceback (most recent call last): File "", line 1, in ? AttributeError: 'module' object has no attribute 'Foo' 

Подобная ошибка возникнет, если мы переименуем модуль persist.py :

Листинг 14. Попытка загрузить консервированный экземпляр переименованного модуля persist.py

 >>> import cPickle as pickle >>> f = file('temp.pkl', 'r') >>> foo = pickle.load(f) Traceback (most recent call last): File "", line 1, in ? ImportError: No module named persist 

Ниже, в разделе Эволюция схемы, мы увидим, как управлять изменениями такого вида, не разрушая существующие консервированные объекты.

Специальные методы состояния

Ранее мы упоминали о том, что несколько типов объектов, как, например, файловые объекты, не могут быть законсервированы. Один из способов управлять атрибутами экземпляра, которые не являются консервируемыми объектами, — это воспользоваться специальными методами, доступными для модифицирования состояния экземпляра класса: __getstate__() и __setstate__() . Ниже приведен пример нашего класса , который мы модифицировали, чтобы управлять атрибутом файлового объекта:

Листинг 15. Обработка атрибутов неконсервируемого экземпляра

 class Foo(object): def __init__(self, value, filename): self.value = value self.logfile = file(filename, 'w') def __getstate__(self): """Return state values to be pickled.""" f = self.logfile return (self.value, f.name, f.tell()) def __setstate__(self, state): """Restore state from the unpickled state values.""" self.value, name, position = state f = file(name, 'w') f.seek(position) self.logfile = f 

Когда экземпляр Foo будет законсервирован, Python законсервирует только те значения, которые возвращены в него при вызове метода __getstate__() этого экземпляра. Подобным образом во время восстановления Python передаст в метод __setstate__() этого экземпляра восстановленные значения в качестве аргумента. Внутри метода _setstate_() мы можем воссоздать файловый объект, опираясь на имя и информацию о положении, которые мы законсервировали, и присвоить файловый объект атрибуту logfile этого экземпляра.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *