Кодинг третьего тысячелетия. Python 3000: обзор нововведенийАлексей Черкес (aleksei.cherkes@gmail.com) Человек всегда стремится к красоте и совершенству. Он старается сделать плоды своего труда идеальными и в этом находит свое счастье. Так устроен мир – и программные технологии здесь не исключение. В рамках этой статьи я предлагаю тебе понаблюдать за развитием языка Python – одного из самых популярных языков программирования на планете.
Языку Python уже больше семнадцати лет, а это весьма почтенный возраст для любой компьютерной технологии. С годами он не устаревает и не подвергается забвению, а наоборот - становится лучше и более востребованным. Третьего декабря 2008 года вышла очередная версия языка - Python 3.0, а четвертого - Python 2.6. Этого события программисты ждали очень долго. Дело в том, что разработчики решили инкрементировать старшую циферку не просто так. Многие новые идеи нельзя реализовать без потери обратной совместимости, а поскольку на питоне уже написана огромная масса кода, то делать это очень не желательно. Тем не менее, разработчики решились на этот шаг. Они выкинули из него весь мусор, освободились от многих устаревших конструкций и добавили несколько нововведений. Лишь бы наш любимый язык становился еще лучше и двигался вперед! Итак, версия 3.0 - это обновленный Python без обратной совместимости. Но зачем тогда было выпускать еще и Python 2.6? Этот релиз нужен для облегчения процесса портирования. Он обратно совместим с предыдущими питонами, и, плюс к этому, содержит многие возможности из третьей версии (включаются по желанию пользователя). Но довольно предисловий – давай рассмотрим, наконец, основные особенности этих релизов. AnnotationsНа практике иногда возникает ситуация, когда к функции необходимо прикрепить некоторую метаинформацию о ее аргументах и возвращаемом значении. Это может быть текст с документацией по аналогии с doc-strings или, например, информация о типе аргумента. В более старых версиях питона для этого использовались различные сторонние библиотеки, которые умели парсить специальным образом оформленный doc-string или использовали декораторы. Был у них и один недостаток - эти средства были нестандартными. Поэтому появилась необходимость введения унифицированного решения. Python 3k для этих целей предоставляет специальный языковой механизм - аннотации. Аннотации - это способ назначения произвольных атрибутов параметрам функции и возвращаемому значению. Обратимся к примеру:
Язык не определяет никакой семантики для аннотаций, что в принципе открывает нам обширную область их применения. По словам разработчиков, они специально не вводили никаких соглашений по их использованию. Делать это надо исходя из реальных потребностей программистов, а их выявлять еще рановато, ведь средство – совсем новое. Однако круг задач, в процессе реализации которых могут пригодиться аннотации, довольно широк: документирование параметров функций и возвращаемых значений (позволит улучшить работу IDE), автоматическая проверка типов (как это делает модуль typecheck с помощью декораторов), перегрузка функций или написание обобщенных функций. Print - теперь функция!В новой версии питона разработчики решили выкинуть ключевое слово print. Но волноваться не стоит: все элементарно! Ключевое слово теперь заменили на функцию. Она определена следующим образом:
Все неименованные параметры преобразуются в строку и печатаются в файл с говорящим названием, в указанном порядке. Между ними каждый раз вставляется строка sep, а в конце добавляется end. Если str или end равны None, то используются значения по умолчанию. Ниже приведено несколько примеров для старых и новой версий – в них печатаются одинаковые последовательности символов:
Очевидно, использование функции вместо ключевого слова упрощает грамматику языка и делает код более читабельным. Полезно это и разработчику: только представь – ты работаешь над огромным проектом, много бессонных ночей, тысячи строк запутанного кода… И вдруг появляется необходимость, скажем, продублировать вывод программы в лог-файл. Если это не было предусмотрено заранее, то старыми средствами выполнить задачу затруднительно. А новыми проще простого! Нужно заменить глобальную функцию на свою – которая может делать все, что тебе угодно. ExceptionsНемного изменился синтаксис обработки исключений. Теперь, чтобы поймать исключение типа TypeError и присвоить его переменной exc, мы должны написать следующее:
Представим, что в этом участке кода программист Вася хотел обработать два типа исключений: ValueError и TypeError. Но получилось у него нечто другое. Согласно старому синтаксису здесь ловится исключение ValueError и заносится в локальную переменную TypeError. Вася будет искать ошибку до тех пор, пока его не уволят. А вот если бы он использовал новый синтаксис, такого печального исхода бы не последовало (ведь слово as визуально отделяет имена типов исключений от переменных, которым они присваиваются).
Появилась возможность создавать цепочки исключений. Они бывают явные и неявные. Если исключение генерируется во время обработки другого исключения (внутри блоков except или finaly), то информация о старом исключении не теряется (как раньше), а сохраняется в атрибуте
Здесь же генерируется исключение With statementЕще одно нововведение - это оператор with. Некоторые объекты, такие как блокировки или файлы, требуют определенных стандартных действий при завершении работы с ними. Это потенциальный источник ошибок, ибо программист – человек, а человек - существо невнимательное и забывчивое. Ситуация осложняется тем, что в таких местах всегда нужно вставлять обработку исключений с блоком finally. Конечно, это громоздкая конструкция. Поскольку она встречается часто и успела порядком поднадоесть кодерам, разработчики решили ввести специальный оператор with, который выполняет черную работу за нас. С использованием нововведений наш пример можно переписать: Гораздо короче! Как это работает? После ключевого слова with должно идти выражение. Его результат - это объект, который называется менеджер контекста (context manager). Этот объект должен содержать методы __enter__() и __exit__(). Метод __enter__() вызывается перед входом в блок команд, идущих после with. Результат присваивается переменной, идущей после необязательного ключевого слова as (в примере файл возвращает сам себя). Метод __exit()__ вызывается после их выполнения, причем (что очень важно), независимо от того – сгенерировано исключение внутри блока или нет. Используя with, ты, во-первых, не забудешь закрыть файл (это делается автоматически в методе __exit__), а во-вторых, тебе не нужно будет писать громоздкую обработку исключений. Многие стандартные типы могут выступать в роли менеджеров контекста – скажем, объекты файлов (как в примере) или межпоточные блокировки. Поэтому эта конструкция найдет широкое применение. Естественно, можно создавать пользовательские менеджеры контекста. Для этого достаточно создать класс и определить в нем методы __enter__() и __exit__(). UnicodeВ версиях питона до 2.0 существовал единственный тип представления строк - str. Для хранения символов использовались только однобайтные кодировки. Никакого юникода тогда еще не было. Тип str использовался как для представления текста, так и для хранения массивов сырых байт. Действительно, в те стародавние времена между этими понятиями не существовало разницы. В ветке 2.x, следуя веяниям времени, разработчики добавили новый тип unicode. Он хранит, как несложно сообразить, строки в многобайтовой кодировке. Из-за обратной совместимости тип str остался в языке – и многие программисты продолжают его использовать по привычке или для упрощения кода. В некоторых случаях юникод-строка могла автоматически преобразоваться в обычную и наоборот, а в некоторых - нет (выбрасывалось исключение UnicodeDecodeError). В Python 3k весь этот бардак ликвидирован. Теперь тип str использует юникод для представления текста (то, что раньше делал тип unicode), а для представления массивов сырых байт введен тип bytes (аналог старого str). Между объектами этих типов позволены только явные преобразования. Они осуществляются с помощью функций str.encode() или bytes.decode(), которые принимают желаемую кодировку в качестве параметра. Забудьте про литералы наподобие u"...". Теперь все строки неявно являются многобайтовыми. Зато появился специальный литерал bytes - b"...". Кодировка по умолчанию для исходных файлов теперь utf-8. Отдельно стоит сказать про функцию открытия файлов open(...). Обращаю твое внимание на разницу между текстовым и бинарным режимами работы. Бинарный режим предполагает использование bytes и не выполняет никаких преобразований перед чтением/записью, а вот текстовый режим преобразовывает байты файла в юникод-стоки и наоборот. Есть и одно забавное изменение, связанное с кодировками – в именах идентификаторов разрешается использовать не ASCII-символы. Можно называть классы, переменные и. т.п. русскими именами! Гвидо долго сопротивлялся этому нововведению, но, видимо, его дожали. Чую, скоро нас завалят тоннами кода с китайскими иероглифами :). PortingСтоит ли прямо сейчас переходить на Python 3.0? Вопрос сложный. Очевидно, что процесс перехода на новую ветку будет довольно медленным, ведь сейчас написаны горы кода, который не будут работать с третьей версией. Гвидо кто-то задал вопрос: «Я собираюсь учить питон с нуля, какую версию лучше для этого использовать?». Мэтр ответил, что лучше все-таки выбрать 2.x ветку, так как пройдет еще год или два прежде, чем она будет окончательно вытеснена новой версией. Однако разработчикам уже сейчас стоит задумываться о портировании своего кода на Python 3.x. Самое интересное, что область пересечения старой и новой веток слишком мала, чтобы комфортно писать код, который бы без изменений работал в обеих версиях. Поэтому процесс портирования может оказаться нетривиальным. К счастью, имеется много подручных средств, призванных облегчить процесс. Одно из них – скрипт с названием 2to3. Идет он в поставке вместе с последними версиями CPython. Скрипт автоматически конвертирует код, написанный для 2.x, в код для 3.x. Где он не справляется, – там выдается предупреждение. На официальном сайте рекомендуют такую стратегию перехода:
FarewellНа этом моя статья заканчивается. Вошло в нее далеко не все, что я хотел бы написать, но журнал не предназначен для того, чтобы в него копипастили документацию! Поэтому тем, кто заинтересовался темой, рекомендую сходить по указанным в выносах ссылкам. Если ты поискал уже везде, где только можно, но так и не познал Дао – шли свои сокровенные вопросы мне на мыло, попробуем разобраться вместе. Безбажного кодинга! WWW
INFO«Модуль» __future__ на самом деле не является модулем. Это просто специальное средство, призванное указывать интерпретатору, чтобы он использовал определенную функциональность языка. |