Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #123, МАРТ 2009 г.

Интимное знакомство с Python

Алексей Черкес (aleksei.cherkes@gmail.com)




«С момента основания Python был важной частью Google, и остается ей по мере того, как система растет и развивается. Множество инженеров Google используют Python, и мы ищем больше людей, умеющих с ним работать» (Peter Norvig, директор по исследованию качества, Google, Inc.)

Еще в январе ты мог заметить, что мы стали посвящать определенное количество журнального пространства замечательному языку программирования под названием «Python». С этого выпуска мы будем публиковать по две тематических статьи за номер: первая – для тех, кто только начинает изучать этот рулезный язык, а вторая – для более-менее продвинутых программеров. Кстати, и Горл, и Никитос – оба записные фанаты Питона ;).

Итак, Python - это высокоуровневый мультипарадигменный язык программирования c динамической типизацией. Он относится к языкам общего назначения. Главный акцент в языке делается на чистоту и читабельность синтаксиса, легкое освоение, минималистичный дизайн, целостную архитектуру. Благодаря этому, программы на Python быстро разрабатываются и легко сопровождаются. Исходный текст в 2, а то и 10 раз короче, чем код аналогичной программы на Java.

Let's play!

Давай запустим интерпретатор Python и немного поиграемся. В статье не будет пространных примеров и объяснений – просто куски кода для скорого ознакомления с языком. Что же мы видим? За символами «>>>» идет текст, вводимый в интерпретатор. После него - ответ интерпретатора (результат вычисления введенного выражения).

>>> 2 + 2
4

Получается, интерпретатор можно использовать вместо инженерного калькулятора на лампах накаливания :).

>>> tax = 12.5 / 100 # Это комментарий
>>> price = 100.50
>>> price * tax
12.5625
>>> price + _
113.0625
>>> round(_, 2)

Вместо символа подчеркивания подставляется последнее выведенное значение. Поддерживается арифметика с вещественными, комплексными числами. Разумеется, «длинная арифметика» тоже поддерживается.

Основы основ

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

>>> # Выведем последовательность Фибоначчи.
... # Элемент равен сумме двух предшественников.
... a, b = 0, 1
>>> while b < 10:
... print b
... a, b = b, a + b
1 1 2 3 5 8

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

Еще одна из мелочей, обуславливающих популярность Python - это кортежи. Обрати внимание, как в примере присваиваются значения переменным. Переменные, перечисленные через запятую, образуют кортеж. Кортежи - это что-то вроде неизменяемых массивов. Их можно присваивать друг другу (как в примере). Чтобы в Python поменять местами значение двух переменных x и y, необязательно вводить третью временную переменную. Достаточно лишь написать x, y = y, x!
Кстати, очень остроумно в нем сделан цикл for. Вот пример:

>>> # Выведем длины всех строк из списка.
... list = ['Python', 'is', 'cool', '!']
>>> for x in list:
... print x, len(x)
Python 6
is 2
cool 4
! 1

В Python цикл for умеет перебирать только элементы коллекций (в некоторых языках для этого есть foreach). Очень изящное решение, не правда ли? List - это список из четырех строк. Мы перебираем их в цикле, выводя на экран. Итерироваться можно не только по встроенным коллекциям. Любой объект, предоставляющий интерфейс итератора, можно писать вместо нашего списка list. А такие объекты встречаются довольно часто, например, итерируясь по объекту открытого файла, мы будем получать строки в нем. Задача печати всех строк в файле займет на одну строку больше, чем этот пример (файл нужно еще закрыть). Перебор диапазона целых чисел (как это обычно делалось в С) - это частный случай описанной схемы. Есть функция range(), которая возвращает коллекцию-диапазон.

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

Играемся с функциями

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

>>> def make(a):
... return lambda x: x**a
>>> f2 = make(2)
>>> f4 = make(4)
>>> for i in [1, 2, 3]: print f2(i)
1 4 9
>>> for i in [1, 2, 3]: print f4(i)
1, 16, 81

Здесь демонстрируется сразу несколько особенностей языка. Рассмотрим их подробнее. В Python можно создавать анонимные (безымянные) функции. Еще они называются lambda-функциями (есть раздел математики, называемый lambda-исчислением, который служит теоретической основой для функционального программирования). Значение выражения lambda x: x**a - это функция, имеющая один параметр x и возвращающая этот параметр, возведенный в степень а (** - оператор возведения в степень). Имя этой новой функции присваивается в точке вызова make. Если этого не сделать, она просто будет удалена сборщиком мусора.
Еще одно важное понятие - замыкания (closures). Замыкание - это функция и переменные, используемые в ее лексическом контексте.

В данном примере наша анонимная функция использует внешнюю переменную «a», хотя она и не передается в нее как параметр. Когда анонимная функция создается внутри make, для нее будет создано замыкание. В него войдет эта функция и значение переменной «а». Если пользователь вызовет эту функцию, значение «a», необходимое для работы, будет извлечено из замыкания и использовано. С каждым новым вызовом функции make создается новое замыкание с разными значениями «a». Назначение функции make - создавать другие функции (!), которые возводят свой единственный аргумент в степень «а». Этот пример лучше освоить, прежде чем читать дальше :).

Как было указано выше, Python - мультипарадигменный язык. Помимо всего прочего, он содержит элементы аспектно-ориентированного программирования (АОП). Об этой парадигме говорят, когда традиционные средства ООП в программе не позволяют выделить некоторую функциональность в отдельный объект или модуль. Такую функциональность называют сквозной. Классические примеры: трассировка вызовов, проверка инвариантов функции (пред- и пост- условия вызова), обработка ошибок и т.п. В программах, написанных на более традиционных языках, изменение функциональности, по которой не происходило разбиение программы на объекты (сквозной функциональности), требует больших затрат, поскольку такие изменения задевают большие участки кода. Декораторы в Python - вот способ решения проблемы. Декоратор – играет роль своеобразной обертки для метода. Рассмотрим пример:

>>> def logger(f):
... def ret( *args, **kwargs ):
... print "enter in", f.__name__
... f( *args, **kwargs)
... print "exit from", f.__name__
... return ret
>>>
>>> @logger
... def foo(x):
... print "foo:", x
>>>
>>> foo("hello")
enter in foo
foo: hello
exit from foo

Функция foo помечена декоратором @logger. Декоратор - это функция или класс, предназначенный для изменения других функций. Например, применение декоратора @logger к функции foo эквивалентно следующей записи: foo = logger(foo). То есть, настоящей функцией foo становится то, что декоратор @logger вернет, получив на вход нашу foo.

Это несложный декоратор, который просто трассирует вызовы функций, выводя на консоль сообщения о входе и выходе из них. Если трассировку нужно выключить, можно заменить декоратор на такой: def logger(f): return f. В других языках такая задача может стать серьезной проблемой, но здесь все делается буквально в два счета

ООП

Несмотря на то, что Python - объекто-ориентированный язык, некоторые аспекты его ООП-модели могут показаться необычными из-за ее динамических свойств. Часто услышать выражение «утиная типизация». Python считает, что, если объект крякает, как утка, и летает, как утка, то можно считать его уткой. Объект тут можно сравнить с мешком для всякого барахла. Содержание мешка определяется во время выполнения программы. В объект можно засовывать дополнительные поля, добавлять методы, заменять их – в общем, делать все, что душе угодно. Конструктор класса создает пустой мешок, запихивает туда кое-какие методы и возвращает программисту. При этом нет возможности узнать, каким классом был сделан мешок, поскольку все мешки «внешне» одинаковые и различаются только содержанием. Когда интерпретатор видит выражение типа obj.f(), он берет мешок с именем obj, роется в нем и, если таковой найдется, выполняет метод f. Если нет – прими ошибку времени выполнения. Это и есть динамическая типизация.

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

Поставляется с батарейками

Общие принципы дизайна Python - простота и эффективность – распространяются и на его стандартную библиотеку. Говорят, что Python поставляется с «батарейками в комплекте» (Batteries Included). Действительно, стандартная реализация (CPython) включает в себя большое множество библиотек, активно применяемых в повседневном кодинге. Просто перечислю:

  • интерфейс к командам операционной системы;
  • работа с файлами и директориями;
  • регулярные выражения;
  • самые разнообразные функции для работы с текстом и строками;
  • навороченное, но удобное форматирование вывода, множество математических функций;
  • длинная арифметика для вещественных и целых чисел (встроена в язык);
  • библиотеки для работы с основными интернет-протоколами (mime, smtp, pop, jsoon, http, ftp, nntp telnet, cookie, cgi и т.д);
  • работа с xml-документами (есть dom и sax-парсеры);
  • сжатие данных (zip);
  • инструменты для профилирования (измерение производительности разных участков программы для выявления узких мест);
  • framework для юнит-тестирования;
  • автоматическая генерация документации;
  • сериализация объектов;
  • библиотека коллекций;
  • многопоточность;
  • работа с базами данных;
  • криптографические сервисы (md5, sha, hmac, hashlib);
  • работа с сетевыми интерфейсами (как прямо через сокеты, так и используя более высокоуровневые абстракции);
  • различные средства взаимодейсвия процессов (IPC); мультимедиа библиотека;
  • сервисы для интернационализации;
  • биндинг c библиотекой Tk (для быстрого создания GUI).

И это только стандартная библиотека, которая идет в одной поставке с интерпретатором! Сложно найти область программирования, в которой нельзя было бы использовать Python. Для решения более сложных задач написано огромное количество сторонних модулей и библиотек. Самое главное, что подавляющее их большинство переносимо на все платформы, на которых работает интерпретатор. Программа на Python, использующая эти модули, будет работать в разных ОС без изменений в коде! Все это превращает Python в идеальное средство для быстрой и эффективной разработки приложений.

Выводы

Сегодня на Python работают тысячи приложений – как большие, сложные и критически важные для ведения бизнеса системы, так и мелкие подручные утилиты, короткие скрипты-эксплойты. Очаровав многих программистов, Python с каждым днем становится все популярнее. Надеюсь, из статьи ты понял, почему большая часть редакции ][акера уже проголосовала за этот язык программирования :).

Основные реализации

Существует довольно много интерпретаторов Python от разных организаций. Ниже приведены основные:

  • CPython - эталонная реализация. Именно над ней работает Гвидо. Суть в том, что для Python не существует никаких официальных стандартов от ISO или ANSI, а в качестве неофициального стандарта выступает CPython. Она написана на чистом С, исходные коды открыты. Команды компилируются в байт-код, который затем выполняется виртуальной машиной. Ее можно найти в *nix-системах, в Win на всяких мобильных телефонах и т.п.
  • Jython, IronPython - реализация на основе JVM. Включает в себя интерпретатор, написанный на Java и компилятор в байт-код. Предоставляет бесшовную интеграцию Java-классов с Python-классами. Есть возможность статической компиляции. Это значит, что свои сервлеты и апплеты ты можешь написать на Python.
  • IronPython - примерно то же самое, что Jython, только для .Net-платформы. Присутствует интеграция .Net-типов в Python-классы и обратно. Возникают некоторые трудности с использование CPython-библиотек.
  • Stackless Python - форк CPython. Оптимизирован для меньшего потребления памяти. Лишен некоторых проблем работы с потоками (GIL). Считается, что эта реализация лучше подходит для использования на платформах с ограниченными ресурсами, такими как микроконтроллеры.
  • PyPy - реализация Python, написанная на Python! Годится для легкой проверки новых возможностей. Позволяет транслировать код в байткоды других виртуальных машин (Javascript, LLVM, CLI и.др).
  • PyS60 - реализация Python для смартфонов фирмы Nokia. Сделана на базе CPython c добавлением новых модулей для работы с Symbian OS.

WWW

Содержание