СЕРГЕЙ СУПРУНОВ
С новым Python 3000!
Сейчас лучше, чем никогда.
Хотя зачастую лучше никогда, чем прямо сейчас.
Тим Питерс, The Zen of Python, PEP-20
(http://www.python.org/dev/peps/pep-0020)
Революция, о которой так долго говорили «пайтонисты», свершилась! В ночь с 3 на 4 декабря 2008 года было официально объявлено о выходе Python 3.0 final, известного также под именами Python 3000 и Py3k.
Язык Python увидел свет в 1991 году и по сей день достаточно активно развивается. Одной из особенностей этого развития всегда являлось стремление разработчиков языка избегать значительных модификаций, сохраняя, насколько это возможно, совместимость с кодом, разработанным для предыдущих версий. На просьбы же внести в язык изменения, способные повлиять на поведение существующих python-программ, Гвидо ван Россум, «главный идеолог» языка, обычно отшучивался, что эти изменения планируется внести в релиз Python 3000, намекая на очень отдалённый год его выпуска. Теперь будущее наступило.
Обзор основных изменений
Новшествами, описанными в этом разделе, список изменений, внесённых в версию 3.0 языка Python, не исчерпывается – здесь приведены лишь наиболее заметные из них. Дополнительную информацию можно будет получить на странице документации: http://docs.python.org/3.0.
Оператора print больше нет
Наиболее серьёзная «утрата», поскольку этот оператор вывода используется практически везде. В 3.0 его уже не существует – для вывода переменных следует использовать одноимённую функцию print(). Представляю, насколько тяжело далось разработчикам языка это решение – сломать такой привычный «инструмент». Но зато функция предоставляет возможность более гибко управлять своей работой с помощью именованных параметров, а также лучше вписывается в общий стиль программы, поскольку позволяет обойтись без «нетрадиционных» синтаксических конструкций вроде:
print >>sys.stderr, 'Error'
Теперь аналогичная задача будет решаться обычной функцией:
print('Error', file=sys.stderr)
Среди прочих параметров – sep, позволяющий изменить символы-разделители нескольких аргументов (по умолчанию – пробел), и end, задающий символы, добавляемые в конец вывода (по умолчанию – символ новой строки):
>>> print(1, 2, 3)
>>> print(1, 2, 3, sep='->', end='!!!\n')
Кроме того, нужно учитывать и ряд отличий в поведении оператора print и новой функции print(). Например, в обоих случаях аргументы выводятся по умолчанию через пробел, однако в операторе поддерживаются так называемые программные пробелы (softspaces), когда реальный пробел отображается только в том случае, если предыдущий символ – не перенос строки. Функция print() отображает пробелы всегда. Пример:
(2.5)>>> print 'Один', 'Два\n', 'Новая строка'
(3.0)>>> print('Один', 'Два\n', 'Новая строка')
Сразу успокою читателей, что в ходе миграции вручную заменять оператор на функцию не придётся – эта задача выполняется специально разработанной утилитой 2to3 (чуть подробнее о ней будет сказано ниже).
Здесь же отмечу, что функция ввода raw_input() переименована в input().
Метод format() вместо «%»
Мы все привыкли к конструкциям типа:
print 'Итого: %d' % total
когда в строке задаются «знакоместа», которые при выводе замещаются значениями переменных, перечисленных в кортеже (tuple) после символа «%». Основным недостатком оператора «%» является то, что это бинарный оператор, т.е. он способен принимать только два операнда. Кроме того, имеет место «перекрытие» некоторых функций данного оператора и типа string, что усложняет реализацию. Поэтому, хотя в версии 3.0 этот синтаксис сохраняется, имеет смысл отвыкать от него уже сейчас, поскольку в Python 3.1 он, скорее всего, будет объявлен устаревшим. Теперь следует использовать более универсальный и гибкий метод format():
print('Итого: {0} {1}'.format(total, currency))
Цифра в фигурных скобках означает порядковый номер позиционного аргумента метода. Можно использовать также именованные аргументы:
print('Итого: {sum} {0}'.format(currency, sum=total)
Помимо просто заполнения знакоместа поддерживаются практически все возможности прежнего форматирования, а также добавлены новые (вдаваться в подробности сейчас не буду, детали можно найти в документе PEP-3101 – http://python.org/dev/peps/pep-3101). На мой взгляд, синтаксис этого метода тоже сложно назвать простым и понятным, но всё же он лучше соответствует общему стилю языка.
В ряде случаев для форматированного вывода удобно использовать встроенную функцию format() вместо print():
>>> format(2/3, '8.3f')
Расширить возможности форматирования можно, определив в своих классах метод __format__(). Также этот метод поддерживается практически всеми встроенными классами:
>>> 1.2.__format__('.5f')
Целые числа
В версии 3.0 прекращено деление целых чисел на «int» и «long» (процесс унификации этих типов был начат достаточно давно, но окончательно завершился лишь с выходом Python 3) – теперь все целые будут обрабатываться как long. Это не могло не сказаться на быстродействии (по словам Гвидо ван Россума, производительность на некоторых тестах упала на 10%), но такую жертву разработчики, видимо, сочли приемлемой ради улучшения «архитектуры» языка.
Кроме того, пресловутая операция деления – «/» – отныне будет вести себя согласно здравому смыслу: теперь «3/5» будет возвращать «0.6», а не «0». Для целочисленного деления следует использовать специальный оператор – «//» (см. врезку «Целочисленное деление»).
Также изменён синтаксис восьмеричных чисел: вместо записи 0640 следует использовать 0o640, по аналогии с записью шестнадцатеричного кода. Чтобы «отловить» возможные ошибки, вызванные привычкой, любые числа, начинающиеся с нуля без последующей буквы, в 3.0 возвращают ошибку синтаксиса:
>>> 0o640
>>> 0640
File “<stdin>”, line 1
0640
^
SyntaxError: invalid token
|
Плюс к этому добавлены формат для записи двоичных чисел – 0b1010 – и соответствующая функция bin().
Дорогу итераторам
В Python 3.0 прослеживается тенденция к повсеместному отказу от списков в пользу итераторов.
Наиболее просто эту концепцию можно пояснить на примере функций range() и xrange(). В версиях 2.x обе функции присутствуют – первая возвращает список целых чисел от нуля до значения, переданного функции в качестве аргумента, вторая – объект класса xrange, ведущий себя подобно итератору:
>>> print range(5)
>>> print xrange(5)
Отличие заключается в следующем: список сразу размещается в памяти, в то время как итератор является особым объектом, который предоставляет значения по мере необходимости. С одной стороны, использование итераторов снижает требования к памяти (особенно для больших списков), но с другой – может увеличивать вычислительную нагрузку и в ряде случаев может сказываться на быстродействии.
Так вот, в версии 3.0 разработчики языка, похоже, решили, что списки есть зло, и очень многие методы, ранее возвращавшие их (например, функции filter(), map(), уже упомянутая range()), теперь возвращают объекты-итераторы. Аналогично ведут себя методы словарей keys(), values(), items(), также возвращающие итерируемые объекты вместо списков (однако нужно иметь в виду, что это не отдельные итераторы, а специальные объекты, отслеживающие изменения в словаре).
При необходимости преобразование в список можно выполнить встроенной функцией list:
>>> d = {'a': 'A', 'b': 'B', 'c': 'C'}
>>> d.keys()
<dict_keys object at 0x00E8FBC0> |
>>> list(d.keys())
Здесь же отмечу, что словари больше не имеют метода has_key() – теперь проверить наличие нужного ключа можно с помощью оператора in:
>>> 'a' in d
>>> 'A' in d
Одним из следствий перехода к итераторам является невозможность применять операцию «среза» непосредственно к результату метода. Да и прочие методы списков (append, count, reverse, sort, extend и т. п.) работать уже не будут. Разработчики языка рекомендуют изменить свои скрипты таким образом, чтобы эти методы не требовались. Если уж совсем никак – используйте функцию list(), чтобы преобразовать итератор в список.
Да здравствует Unicode!
До версии 3.0 исходный код на языке Python рассматривался интерпретатором по умолчанию как ASCII-текст. Национальные символы вызывали ошибку интерпретатора, и для их использования кодировку требовалось указывать явно:
#!/usr/local/bin/python
# -*- coding: koi8-r -*-
В новой версии основным стандартом кодирования является Unicode. Следствия этого:
- Исключён префикс u'' для строк в Unicode.
- Добавлен префикс b'' для «бинарных» строк, которые будут интерпретироваться побайтно.
- Допускается использование символов национальных алфавитов не только в строковых константах, но и в именах переменных, функций, методов и т. п.
- При чтении файлов по умолчанию будет выполняться преобразование в Unicode; для считывания «сырого» содержимого файла следует открывать его с явным указанием режима «b»:
open(file, 'rb')
- Пользователям UNIX-подобных систем следует более внимательно относиться к переменной окружения LANG (или LC_CTYPE) – именно ей интерпретатор будет руководствоваться при работе с именами файлов, заданных в национальной кодировке, и в ряде других случаев. (Пользователям Windows чуть проще, так как эта ОС уже хранит имена файлов в Unicode.)
- Функция repr() и метод строк __repr__() больше не выполняют преобразование «неASCII»-кода в escape-последовательности (в Python 2.5 функция repr('строка') возвратит «'\\xe1\\xe2\\xe0\\xae\\xaa\\xa0'», в то время как в 3.0 – «'строка'»). Escape-последовательности сохранились только для управляющих символов – «\n», «\t» и ряда других.
Подробности вы найдёте в следующем документе: http://docs.python.org/3.0/howto/unicode.html#unicode-howto.
Никаких исключений для исключений
Исключения (Exceptions) стали полностью «объектно-ориентированными». Теперь даже оператор raise требует в качестве аргумента только объект, являющийся потомком по крайней мере класса BaseException; использование простых строковых сообщений не допускается (хотя и в 2.5 оператор «raise 'Error message'» уже выполняется с предупреждением):
>>> raise 'Error message'
__main__:1: DeprecationWarning: raising a string exception is deprecated
Traceback (most recent call last):
File “<stdin>”, line 1
TypeError: exceptions must derive from BaseException
|
>>> raise Exception, 'Error message'
File “<stdin>”, line 1
raise Exception, 'Error message'
^
SyntaxError: invalid syntax
|
>>> raise Exception('Error message')
File “<stdin>”, line 1, in (module)
Exception: Error message
|
Естественно, вы можете использовать и собственные классы исключений, но они обязательно должны являться наследниками класса BaseException (как правило, в общих случаях в качестве базового используют класс Exception).
Обратите внимание и на некоторые изменения синтаксиса. В Python 2.x вы могли вызвать исключение следующим образом:
raise Exception, message, traceback
где:
- Exception – объект-исключение;
- message – выводимое сообщение об ошибке;
- traceback – вывод обратной трассировки.
Теперь message следует передавать как аргумент Exception, параметр traceback больше не поддерживается (если он вам нужен, можете переопределить атрибут __traceback__ в классе исключения):
raise Exception(message)
Также в операторе «try – except» вместо:
except Exception, var
нужно использовать синтаксис:
except Exception as var
Теперь поддерживаются так называемые цепочки исключений – при возникновении исключения в блоке except или finally оператора «try – except – finally» доступ к первоначальному исключению можно получить через атрибут __context__.
Если в некоторой переменной (допустим, exceptvar) сохранён объект исключения, то цепочку исключений можно вызвать и явно:
raise Exception() from exceptvar
В этом случае первоначальное исключение exceptvar сохраняется в атрибуте __cause__ объекта Exception.
Закат «классических» классов
Изначально в языке Python классы и типы, несмотря на внешнее сходство, имели различное внутреннее устройство, что не позволяло выполнять наследование от типов. В Python 2, дополнительно к этим «классическим» классам, были добавлены «новые» – чтобы использовать их возможности, требовалось явное наследование от класса object. Теперь «классические» классы исключены из языка, и любой класс (в том числе и пользовательский без явного указания родительского класса) работает как «новый».
Прочие «мелочи»
Появился синтаксис «расширенной распаковки», упрощающий развёртывание списка или значений итерируемого объекта в отдельные переменные. Пример:
>>> s = 'serg:*:1006:1006:Suprunov:/home/serg:/usr/local/bin/bash'
>>> login, *tmp, shell = s.split(':')
>>> login
>>> shell
>>> tmp
['*', '1006', '1006', 'Suprunov', '/home/serg'] |
То есть с начала и/или с конца списка можно выбрать нужные значения, а всё остальное записать в один список. Особенно этот синтаксис удобен, когда приходится разбирать строки с неопределённым числом элементов, в которых нужны лишь один-два первых элемента.
Исключён модуль sets, осуществлявший работу с множествами. Вместо него нужно использовать встроенный тип set:
tariffs = set(('Повремённый', 'Комбинированный', 'Безлимитный'))
То же самое можно сделать и с использованием нового синтаксиса для множеств:
tariffs = {'Повремённый', 'Комбинированный', 'Безлимитный'}
Исключён оператор неравенства «<>». Теперь поддерживается только «!=».
Операции сравнения несовместимых типов (например, числа со строкой) теперь будут возвращать ошибку TypeError.
Расширился список зарезервированных слов. К нему добавлены: nonlocal, True, False, None, as, with и прочие. Не забывайте сверяться с документацией.
Ряд стандартных модулей сменил название на более соответствующее общепринятым соглашениям. Например, ConfigParser переименован в configparser, SocketServer – в socketserver, и т.п. Некоторые близкие по функциональности модули объединены в пакеты (например, модули BaseHTTPServer, SimpleHTTPServer, CGIHTTPServer, httplib, Cookie, cookielib теперь объединены в пакет http, включающий модули client, server, cookies и cookiejar).
Вопросы миграции
Как видите, изменения довольно серьёзные, и ваш нынешний код с вероятностью 99,9% в Python 3.0 работать не будет:
- если вы используете оператор print, интерпретатор вернёт ошибку;
- если вы не прислушивались к рекомендации использовать для целочисленного деления оператор «//» вместо «/», то ваш код может работать неправильно;
- если вы активно используете вывод методов словарей keys(), values() и т. п. именно как списки, ваш код не будет работать;
- многие функции, связанные с преобразованием строк в разные кодировки, будут работать неправильно либо не будут работать вообще;
- даже если ни под один из вышеприведённых случаев ваш код не подпадает, наверняка найдётся ещё что-нибудь, что не позволит ему нормально работать в Python 3.
Таким образом, вам придётся выбирать между двумя альтернативами: продолжать использование 2-й версии языка либо модифицировать свой код под новые синтаксические требования.
Первая альтернатива целесообразна для больших проектов, которые находятся в высокой степени готовности либо уже работают. Также нет особого смысла переносить на новую версию код, жизненный цикл которого не превышает одного-двух лет, поскольку полноценная поддержка 2-й версии языка будет продолжаться ещё довольно долго. Кроме того, не следует сбрасывать со счетов некоторую «сырость» интерпретатора версии 3.0, а также запаздывание поддержки версии 3.0 в библиотеках и модулях сторонних разработчиков (например, на момент подготовки статьи библиотека Python Image Library для Python 3.0 на сайте разработчика не упоминалась).
А вот для проектов с продолжительным жизненным циклом, находящихся на начальных этапах разработки, имеет смысл осуществить миграцию на новую версию и вести дальнейшую разработку уже с учётом её особенностей.
Что же касается небольших скриптов, то здесь вопрос сводится, скорее, к личному интересу – если есть желание «поиграться» с новой версией, то можно заняться миграцией. Если вы сторонник принципа «пока работает – не трогай», то можно смело подождать момента, когда ваши скрипты «перестанут работать», а это произойдёт ещё очень не скоро.
Если вы всё-таки решили выполнять миграцию, то Гвидо ван Россум рекомендует следующую последовательность действий:
- Желательно использовать хорошие автоматические тесты.
- Обновиться до версии 2.6 и добиться безошибочной работы вашего кода с этой версией интерпретатора. Поскольку в рамках 2-й ветви разработчики придерживаются принципа обратной совместимости, то серьёзных проблем у вас на этом этапе возникнуть не должно.
- (Все еще с Python 2.6.) Добавить ключ «-3» в командах вызова интерпретатора, то есть из командной строки запускать скрипты командой:
python -3 script.py
Благодаря этому интерпретатор переключится в режим предупреждения о несовместимости с версией 3.0, и на консоль вы будете получать сообщения о тех синтаксических конструкциях, которые нужно будет модифицировать. Здесь придётся поработать, корректируя код до тех пор, пока не останется ни одного предупреждения.
- Теперь вы готовы к миграции. Пропустите ваш код через специальную утилиту 2to3 (её вы найдёте в каталоге Tools/scripts дистрибутива, описание использования можно почитать по адресу http://docs.python.org/3.0/library/2to3.html#to3-reference), которая выполнит большинство рутинных синтаксических преобразований, таких как замена оператора print функцией print().
- Настало время пробовать запустить код под интерпретатором 3-й версии. 100-процентную гарантию безошибочности вам никто не даст, поэтому всестороннее тестирование по-прежнему необходимо. Однако здесь корректив должно потребоваться немного.
В награду за выполненную работу вы получите возможность пользоваться преимуществами новой версии (а также терпеть её недоработки, если таковые будут выплывать).
Не останавливаясь на достигнутом...
Выпустив релиз 3.0, команда разработчиков продолжает движение вперёд, и уже можно начинать отслеживать изменения, вносимые в следующий выпуск 3.1 – http://docs.python.org/dev/py3k/whatsnew/3.1.html. Ожидается, что основным акцентом этого релиза станет оптимизация кода. Также ведётся разработка следующей версии во 2-й ветви – 2.7. Никаких потрясений, связанных с этими релизами, не ожидается, так что, единожды приняв решение о миграции, дальше можно жить спокойно.
Приложение
Целочисленное деление
Одной из наиболее обсуждаемых особенностей Python являлось «целочисленное деление». До версии 3.0 оператор «/», оба операнда которого являлись целыми числами, возвращал также целое число (как это происходит в языке Си). То есть выражение «2/4» давало в результате «0» вместо ожидаемого многими «0.5». Особенно это выглядело нелогичным с учётом динамической типизации в Python. Проблему признавали многие, в том числе и ван Россум, поэтому уже с версии 2.2 в язык был добавлен явный оператор целочисленного деления – «//», и программистам рекомендовалось использовать в своём коде именно его, избегая оператора «/». Это должно было сделать перевод данного оператора в будущих версиях на «действительное» поведение достаточно безболезненным, однако в пределах одной ветви на такое новшество команда разработчиков не решалась. В Python 3.0 этот шаг был сделан, и теперь случайных и трудно отлавливаемых ошибок в коде должно стать немного меньше.