Журнал ВРМ World

Мировая история развития технологий управления эффективностью бизнеса – обзоры зарубежных публикаций

Почему Python сжимает свое кольцо вокруг компаний

Мы возобновляем рубрику "Язык программирования Python" и предлагаем вашему
вниманию интервью с одним из автором "Справочного руководства по Python"
("Python Cookbook")
Алексом Мартелли (Alex Martelli).

Примечание редактора: поскольку Sun Microsystems и Microsoft теперь освещаются в СМИ как вновь обретенные "партнеры", и обе компании будут встраивать скриптовые языки в Web-сервисы, которые управляются Java и .Net, язык с открытым исходным текстом Python неожиданно приобретает дополнительное стратегическое значение. Наш коллега из Open Enterprise Trends взял интервью у Алекса Мартелли (Alex Martelli), автора двух книг, изданных в издательстве O'Reilly Associates и посвященных корпоративному использованию Python - "Python в двух словах" ("Python in a Nutshell") и "Справочное руководство по Python" ("Python Cookbook").


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


OET: Вы отмечаете сходство между .NET, Java и C при сравнении с Python. А если ли значимые различия, которые могли бы запутать часть разработчиков?

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

Мощные средства, такие как регулярные выражения, играют совершенно иную роль в отличие от основных средств языка - присваивания, передачи аргументов, возбуждения и обработки исключений, возврата значений из функций. Python включает в собственные средства языка только те концепции и функциональности, которые на самом деле полагается включать (в том числе некоторые мощные типы данных общего назначения, такие как строки, списки [которые, несмотря на свое имя, более похожи на динамические вектора или массивы других языков] и словари [полностью общие и высоко оптимизированные таблицы данных]).

В Python (во многом как и в C, C++, Java и .NET) все, что можно наиболее рационально поставлять с помощью библиотечного модуля, хранится в библиотечном модуле, будучи всегда доступным для импорта при надобности и когда необходимо, не усложняя, однако, без нужды сам язык. Регулярные выражения, например, находятся в Питоновском стандартном библиотечном модуле re. Если, и когда, разработчику необходимо воспользоваться их мощью, они все там находятся - но часто бывает достаточно простого манипулирования строками, поскольку строки Python обладают весьма широкими и мощными возможностями.

Рассмотрим, например, недавно выпушенную версию Python 2.3. Если вам нужно определить, является ли одна строка (по имени needle) частью другой (по имени haystack), нет необходимости вытаскивать регулярные выражения со всей их мощью - только код:


	if needle in haystack:
	print 'found'
	else:
	print 'not found' 

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


	import re

	re_needle = re.compile(r'b%sb'%re.escape(needle),re.IGNORECASE)
	if re_needle.search(haystack):
	print 'found with all the trimmings'
	else:
	print 'not found'

Разумеется, здесь присутствует некий элемент обучения, особенно если вы никогда не использовали регулярные выражения. Но вам придется просто пройти это обучение, если, и когда, вам необходимы мощные возможности регулярных выражений по поиску в строке; во многих областях приложений они могут никогда не потребоваться.

OET: Какими базовыми навыками на языке Python должен обладать разработчик, чтобы начать свой первый проект?

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

Например, вам не нужно "баловаться" с регулярными выражениями до тех пор, пока и если только вам не требуются продвинутые возможности синтаксического разбора текстов, точно также вам не нужно знакомиться с другими модулями стандартных Питоновских библиотек (с помощью которых можно легко реализовать Web-серверы, Web-клиентов, обработку электронной почты, серверы и клиенты распределенной обработки XML-RPC и тому подобное) до тех пор, пока и если только вам не требуется эта специальная функциональность.

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

OET: Нужно ли разработчикам разбираться в объектно-ориентированном программировании, чтобы использовать Python?

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

Встроенные объекты Python, такие как строки, списки, файлы и словари, предлагают методы - например, поименованные функции, которые вы можете вызывать, чтобы получить доступ к их функциональности. Например, при наличии списка в переменной с именем foo, вы можете вызвать foo.sort(), чтобы отсортировать этот список на месте; вы можете вызвать foo.index(23), чтобы установить в списке индекс числа 23 (если число 23 отсутствует в этом списке, то будет возбуждено исключение).

Любой программист легко поймет идею вызова таких методов; например, somelist.index(anitem), чтобы отыскать элемент в списке - затем, оттуда, вы можете вполне естественно перейти к написанию своих собственных классов объектов, реализуя аналогичные методы (и вполне возможно к заданию производных классов от существующих типов, чтобы добавить или подстроить функциональность). Python предлагает программисту объектную ориентированность не как препятствие, которое нужно преодолеть, а как еще один полезный инструмент для выполнения работы.

OET: Не могли бы вы рассказать о некоторые заслуживающие внимания сходствах и различиях между работой на Python и на других языках?

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

Но основное различие в этой связи между Python и Java заключается в том, что используя Python вам никогда не потребуется явно просить Python компилировать ваши исходные файлы в файлы байт-кода: Python сделает это автоматически и неявно, если, и когда, он не увидит ни одного файла байт-кода, соответствующего исходному файлу для модуля, который он должен импортировать, либо когда файл байт-кода существует, но он старше исходного файла. В некотором смысле, это как будто бы в Python был встроен неявный автоматический make (make - это популярное средство не только на Unix, но и на Windows, в версиях, таких как nmake, это часть продукта Microsoft Visual Studio), и поэтому вам по сути не нужно беспокоиться о компиляции.

Еще параллели Python-Java: все в Python наследуется из 'Object', точно как в Java все наследуется из 'object'. Как я уже упоминал, Python располагает последовательной объектно-ссылочной семантикой, точно как и Java, и на самом деле даже более чем Java (в том, что та же самая семантика также применяется к числам, которые в Python неизменяемые (immutable), как и строки). Более того, Python, как и Java, поставляется с чрезвычайно большой и богатой стандартной библиотекой и предоставляет встроенную и/или библиотечную поддержку для такой богатой функциональности, как интроспекция, сериализация и многонитевые операции.

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

Еще параллели Python-C++: Python, как и С++, поддерживает множественное наследование и перегрузку операторов. Больше всего Python, как и С++, предлагает вам иногда потрясающее разнообразие выбора в том, что касается поддержки технологий: например, и для классического Python, и для C++ существует не одна единственная "официальная" библиотека графического пользовательского интерфейса (GUI), а десятки - и бесплатных, и коммерческих, и зависимых от платформы, и межплатформенных. Разумеется, из такого потрясающего разнообразия выходит только несколько "лучших в своем классе"; например, лучший выбор среди межплатформенных библиотек GUI для C++ - это wxWindows, если вы желаете бесплатный инструмент, и Qt - если коммерческий.

Неудивительно, что лучший выбор для Python - это wxPython и PyQt, точные копии своих эквивалентов для C++, соответственно. (Библиотека GUI, которая поставляется с Python, Tkinter, о которой я рассказал в "Python в двух словах", широко применяется, поскольку она очень распространена, и ее весьма легко использовать, но я бы не рекомендовал ее для коммерческих приложений, так как, на мой взгляд, wxPython и PyQt лучше).

OET: Вы говорите так, как будто для программистов C разработка на Python окажется легким делом.

Мартелли: В некоторой степени Python удивительно поход на C - на самом деле Python ближе по сути к C, чем C++ или Java, несмотря на то, что внешне синтаксис и C++, и Java ближе к C, хотя синтаксис Python более четкий и понятный. Логическое обоснование C, принадлежащее Национальному Институту Стандартизации США (ANSI), выражает "Суть C" очень хорошо и точно в соответствии со следующими пятью пунктами:

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

Python строго отвечает первым четырем пунктам и следовательно, можно сказать, соответствует на 80 процентов "Сути C" - что гораздо больше по сравнению с якобы прямыми последователями C - Java и C++. Общеизвестно, что скорость отнюдь не является столь важной целью для Python, как для C (и C++), поэтому пятый пункт выполняется очень незначительно - но 4 из 5 - это не слишком плохо.

Во многих отношениях, Python, разумеется, радикально отличен от C, C++ и Java. Основное отличие, я бы сказал, состоит в том, что C и C++ оптимизированы для работы машины, а Python - для работы программиста - программы Python зачастую не будут такими быстрыми как те, что вам удастся создать на C, однако, программируя на Python, вам может потребоваться десятая часть времени, которое необходимо, чтобы написать код для той же функциональности на C.

Эти различия можно распределить по трем техническим категориям:

  • Четкий синтаксис - минимизация "графического мусора" ("пиксели, которые не несут никакой информации", согласно незабываемым книгам Э. Тафта (E. Tufte) о визуальном отображении информации). Блоки не содержат скобки - только отступы - например, ни if> или while не используют круглые скобки вокруг условий.
  • Строгая, но динамическая типизация - объекты имеют типы (строгие типы, а не такие слабые, как в C или C++ - в Python, как и в Java, вы не можете просто использовать приведение, чтобы "принудительно интерпретировать эту группу битов, как будто бы она типа X, а не Y"), а имена - нет, имена - это просто имена, и они могут быть присоединены к объектам различных типов; следовательно нет "объявлений", только исполняемые директивы (каждый фрагмент кода что-либо делает, а не существует просто для того, чтобы "сообщить" компьютеру о каких-либо характеристиках программы).
  • Почти все - это объект первого класса - он включает, например, классы, функции, методы, модули, пакеты, таким образом, вы можете использовать каждого и любого из них в качестве, например, аргументов функций, элементов списков, величин, возвращаемых методами - количество "шаблонов проектирования" ("design patterns"), стереотипов (boilerplate) и общей "возни", устраненного благодаря этому удивительно простому и мощному проектированию, почти невероятно.

OET: Судя по вашей книге, вы сторонник Python DBAPI. Не могли бы вы привести короткий пример того, как программист/администратор базы данных может воспользоваться этой технологией, чтобы решить общие задачи доступа к базе данных?

Мартелли: Питоновский интерфейс прикладного программирования (Python DBAPI) баз данных определяет универсальный интерфейс, который позволяет программам Python обращаться к реляционным базам данных, и многие модули третьих сторон реализуют этот интерфейс, присоединяясь ко всем видам различных процессоров баз данных, включая и коммерческие (такие как Oracle, IBM DB/2, Sybase...), и базы данных с открытым кодом (такие как PostgreSQL, MySQL, SAP/DB…), и упрощенные реализации реляционных баз данных, как, например, Gadfly (которая написана на Python) и SQLite (бесплатная библиотека, которая позволяет встраивать функциональные возможности SQL непосредственно в любую вашу программу).

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

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

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

OET: В этой связи, какие элементы Python были бы интересны администратору базы данных, который собирается создать с помощью Python функции совместной обработки разнородных БД?

Мартелли: Универсальность DBAPI на уровне программы, разумеется, окажется очень полезной в любом случае, когда администратору базы данных потребуется обеспечить обмен данными между двумя различными базами данных - не то, чтобы это было очень распространённой задачей, но постепенно приобретает популярность по мере того, как различные предприятия объединяют либо разделяют свои операции.

Кроме того, даже когда администратору базы данных нужно беспокоиться только об одном определенном процессоре базы данных, простота и мощь DBAPI должна помочь - в этом случае корректно устранив проблему "как мне присоединиться к базе данных, обращаться к ней с запросом и обновлять ее", сведя ее к написанию кода для соответствующих директив SQL - как только это сделано, администратор базы данных будет способен воспользоваться мощью Python, чтобы обрабатывать, отображать, изменять данные и манипулировать ими любым необходимым образом, используя условную или итеративную обработку, сводные итоги, взаимнокорреляционные таблицы, статистический анализ и тому подобное.

Использование модуля, совместимого с DBAPI, сводится к нескольким простым шагам, как только выбраны и установлены модули, отвечающие потребностям базы данных.

OET: Не могли бы вы в общих чертах рассказать, как разработчик мог бы построить программу, используя один из этих модулей, "удобных для DBAPI"?

Мартелли: На самом деле, они называются "модулями, совместимыми с DBAPI", и, разумеется, я о них расскажу. Во-первых, полезно понимать, что такая модель содержит функцию 'connect', которая принимает несколько аргументов (таких как 'database' для имени определенной базы данных, к которой нужно присоединиться, 'user' и 'password' для идентификации и тому подобное) и возвращает объект, который является экземпляром класса Connection.

Методы в экземпляре Connection контролируют транзакции (фиксацию и откат) и создают 'cursors'; например, экземпляры класса Cursor (DBAPI обеспечивает, что такие cursors могут быть получены даже для баз данных, которые обычно их не содержат, как, например, MySQL). В свою очередь, методы в экземпляре Cursor выполняют директивы SQL (и запросы, такие как SELECT, и операции по обновлению базы данных, такие как UPDATE), факультативно принимая значения параметров из данных, передаваемых Python, и выдают результаты, преобразованные в обычные используемые Python данные соответствующих типов.

Таким образом, типичная последовательность операций могла быть иметь следующий вид:


	import [insert appropriate module here] as DB

	connection = DB.connect( [insert appropriate parameters here] )
	cursor = connection.cursor()
	cursor.execute('SELECT a, b FROM table WHERE d=%s', datum)

	while True:
	try: a, b = cursor.fetchone()
	except DB.Error: break
	process(a, b)

OET: Как мог бы разработчик начать использовать атрибуты Python, чтобы облегчить поддержку некоторых проектов Web-сервисов (XML, порталы, интеграция баз данных, библиотека GUI, определенная пользователем)?

Мартелли: Проекты Web-сервисов, где Python проявит себя наиболее ярко, это, возможно, наиболее претенциозные проекты, те, которые основываются на пользовательских сетевых серверах или клиентах с высокими показателями масштабирования и доступности. Python исключительно хорошо поддерживает протокол XML-RPC для Web-сервисов, хотя также доступна поддержка SOAP (пока не в стандартной библиотеке Python, а в бесплатных модулях третьих сторон).

Для любого Питоновского сетевого сервера или клиентского приложения можно посоветовать разработчику бесплатный пакет Twisted Matrix, который позволяет объединить специализированные, пользовательские сетевые сервера на чистом Python с легкостью, гибкостью и отличной производительностью и масштабируемостью. Действительно, причина, по которой Python, вероятно, будет выделяться в самых сложных проектах, просто состоит в том, что истинная сложность таких проектов может потребовать всю мощь Python, активно поддерживаемую Twisted, "продвинутыми" инструментами обработки XML, интерфейсами к базам данных, библиотеками GUI, определенными пользователем, и тому подобным.

OET: Что это за функциональные возможности Twisted Matrix, которые так ценны для разработчиков Web-сервисов?

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

Вся архитектура Twisted основывается на базовой концепции асинхронных (также известных как управляемых событием) операций (хотя при необходимости многонитевая обработка также может быть гладко интегрирована), которая на самом деле является "секретным оружием", позволяющим получать очень хорошую производительность и масштабируемость без потери гибкости и функциональности - реализация событийного цикла (технически известная как "реактор" ("reactor")) в центре схемы может в свою очередь быть выбрана среди палитры реализаций, которые оптимизируют интеграцию с другими системами, такими как библиотеки GUI для клиентских операций или чистую производительность сервера в зависимости от базовой операционной системы.

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

Выберите ли вы автономную (Twisted) архитектуру или основанную на размещении кода Python в существующем сервере или оболочке, вы всё же получите всю мощь Python, доступную для ваших частных потребностей в обработке, и множество полезных библиотечных модулей: стандартных, поставляемых вместе с Python, и мощных добавляемых для расширения модулей от третьих сторон, когда это необходимо.

Совершенно отдельная возможность - это Zope, который некоторые считают Питоновским "потрясающим приложением" - прикладной сервер с открытым кодом, оптимизированный для порталов, приложений, определенных пользователем, и для управления контентом, полностью написанный на Python и легко расширяемый в Python и в любом направлении, которое вам может понадобиться. Связь Zope с Python очень сильная - в настоящее время многие из основных разработчиков Python, включая главного архитектора Python, Гвидо ван Россума (Guido van Rossum), работают в Zope Corporation.

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

OET: Какие преимущества может предоставить Python в традиционных средах Win32? Не могли бы вы рассказать о них в контексте с технологиями ADO, COM и ASP?

Мартелли: Python на платформе Win32 может особенно хорошо интегрироваться с COM - и, по этому самому лёгкому пути - практически со всем, что запускается на Win32, включая приложения, бесплатные и коммерческие компоненты, системные сервисы и инфраструктуру. Для этого вам необходим классический Python и добавляемые расширения win32all (вы можете бесплатно скачать классический Python, входящий в сборку win32, которая уже интегрирует win32all и другие полезные компоненты, с ActiveState или вы можете начать со стандартным Питоновским дистрибутивом win32, получить и установить win32all отдельно).

Как только у вас есть классический Python с расширениями win32all, вы получаете полный доступ к COM и всем низкоуровневым интерфейсам прикладного программирования Win32 - плюс, разумеется, все другие мощные Питоновские возможности. Например, вы можете получить модуль, который поддерживает интерфейс DBAPI и внутренне реализуется поверх ADO, таким образом, вы можете программировать для источников данных с интерфейсом ADO, не теряя переносимость и простоту Питоновского DBAPI; в качестве альтернативы вы можете использовать ADO непосредственно из вашего кода Python (разумеется, если для вас несущественна переносимость).

Аналогично, вы можете продолжить использовать Питоновские типичные межплатформенные библиотеки GUI (немного скудный, но очень простой Tkinter, богатый, мощный и бесплатный wxPython или невероятно мощный коммерческий PyQt), или вы можете использовать Питоновский интерфейс к классической библиотеке базовых классов Microsoft (MFC) (известной как PythonWin и включенной в win32all) - вы можете интегрировать управляющие элементы (Controls) ActiveX в интерфейс, как вы могли бы в Visual Basic, и т.д.

Если вы программируете серверную Web-логику на Python, вы можете интегрироваться с информационным сервером Internet (IIS) через протокол ASP (поскольку с помощью win32all Python реализует стандарт Microsoft "Active Scripting") вместо использования собственных более мощных Питоновских подходов, таких как инструментарий Webware (который может интегрироваться с IIS, Apache и другими серверами и предоставляет богатый выбор с точки зрения архитектур для написанных на Python Web-страниц, включая схожее с протоколом ASP встраивание Питоновского кода непосредственно в HTML, "сервлеты", наподобие знакомых по Java и/или методам создания шаблонов, которые часто обеспечивают решение, которые будет легче всего сопровождать и расширять для проектировщиков Web-сайтов, не являющихся программистами).

Когда реально, я бы посоветовал выбрать Питоновские архитектуры (такие как Webware для Web-сайтов, PyQt для библиотек GUI, встроенные модули DBAPI для установления связи с SQL и т.д.), но Питоновская сверхъестественная способность проникать в каждую технологическую нишу может позволить применять его в ситуациях, основанных на потребности расширить существующие системы, где ограничения по выбору технологии и архитектуры препятствуют использованию языков, которые определены для предоставления своих собственных предпочтительных архитектур и технологий.


Ванс Маккарти (Vance McCarthy) - редактор Open Enterprise Trends; эта статья появилась благодаря специальной договоренности с OET.


Ссылки

  1. Ресурс "Python" - http://www.python.org/
  2. Ресурс "Open Enterprise Trends" - http://www.oetrends.com/
  3. Издательство "O'Reilly Associates" - http://www.oreilly.com/
  4. "Python в двух словах" ("Python in a Nutshell") - http://www.oreilly.com/catalog/pythonian/
  5. "Справочное руководство по Python" ("Python Cookbook") - http://www.oreilly.com/catalog/pythoncook/

Автор: Ванс Маккарти (Vance McCarthy)