Пользователь
0,0
рейтинг
9 июля 2012 в 10:21

Разработка → Мысли о Python 3 из песочницы

Предлагаю вашему вниманю пересказ замечательной статьи автора Jinja2, Werkzeug и Flask, соавтора Sphinx и Pygments Армина Ронахера. Я получил огромное удовольствие разбирая исходные коды его творений и очень многое для себя почерпнул. Армин пишет отличные фреймворки, и как никто другой может разъяснить, чем чреват переход с Python 2 на Python 3 и почему его не так легко осуществить.



Мысли о Python 3


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

Заранее предупреждаю: это очень личная статья. Я насчитал сотню экземпляров одной заглавной буквы в этом тексте.

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

А с другой стороны, хоть я и люблю Python и люблю обсуждать пути и решения, я не связан с проектом какими-либо обязательствами, несмотря на преданность ему. Когда я посещаю совещания о языке, то сразу понимаю, почему мои предложения воспринимаются в штыки, а сам я считаюсь ещё той занозой. «Постоянно жалуется и ничего не делает». Есть столько всего, чего бы мне хотелось видеть в Python, но в конце концов, я его пользователь, а не разработчик.

Когда вы будете читать мои комментарии о Python 3, учитывая что его первая версия уже выпущена, у вас сложится впечатление, что я его ненавижу и вообще не хочу на него переходить. Ещё как хочу, но только не в той форме, в которой он сейчас находится.

Учитывая мой опыт того, что люди ссылаются на статьи спустя много времени после того как они были написаны, позвольте вначале разъяснить ситуацию с Python 3 на момент написания: вышла версия 3.2, следующая версия 3.3 и нет никаких планов когда-либо выпустить Python 2.8. Более того, существует PEP, в котором чёрным по белому написано: релизу не быть. Прекрасно развиваясь, PyPy остаётся проектом, архитектура которого настолько отдалена от всего остального, что его ещё долго никто не будет воспринимать всерьёз. Во многом PyPy делает вещи которые «я бы не сделал» и это мне кажется удивительным.

Почему мы используем Python?

Почему же мы используем Python? Мне кажется, что это очень правильный вопрос, который мы редко себе задаём. У Python есть куча изъянов, но я им всё-равно пользуюсь. На вечеринке, в последний день конференции PyCodeConf этого года, я успел многое обсудить с Ником Кофланом. Мы были подшофе и благодаря этому дискуссия получилась очень искренней. Мы согласились признать факт того, что Python не идеален, как язык, что над некоторыми изъянами продолжается работа и что при внимательном рассмотрении, некоторым из них нет оправданий. Был рассмотрен PEP о «yield from» как пример развития сомнительного дизайна (coroutine как generator) для придачи ему более-менее рабочего вида. Но даже с изменениями принятыми в «yield from», всё это очень далеко от удобства greenlet'ов.

Этот разговор был продолжением услышанного на лекции «Предвзятое мнение о языках программирования», которую читал Гери Бернард в тот же памятный день конференции. Мы пришли к общему мнению о том, что у блоков Ruby восхитительный дизайн, но по многим причинам он не прижился бы в Python (в его текущем состоянии).

Лично я не думаю, что мы используем Python потому, что это совершенный и безупречный язык. Более того, если вы вернётесь в прошлое и присмотритесь к ранним версиям Python то увидите, что он очень и очень уродлив. Не стоит удивляться тому, что в свои ранние годы Python оставался никем незамеченным.

Мне кажется, что размах полученный Python с тех пор, можно считать большим чудом. И вот почему, как мне кажется, мы используем Python: эволюция этого языка была очень плавной, а воплощённые идеи — верными. Ранний Python был ужасен, в нём отсутствовала концепция итераторов и более того, для итерации по словарю приходилось создавать промежуточный список всех его ключей. В какой-то момент исключения были строками, методы строки были не методами а функциями из одноимённого модуля (string). Синтаксис перехвата исключений мучает нас во всех ипостасях языка Python 2, а Юникод появился слишком поздно и частично — никогда.

Однако, в нём было и много чего хорошего. Пускай и небезупречная, идея о модулях с их собственными пространствами имён была восхитительной. Структура языка основанная на мультиметодах* до сих пор во многом не имеет себе равных. Мы ежедневно выигрываем от этого решения, хоть и не отдаём себе в этом отчёта. Этот язык всегда честно делал свою работу и не скрывал происходящее в интерпретаторе (traceback'и, кадры стека, опкоды, кодовые объекты, ast и т.д.), что вкупе с динамической структурой позволяет разработчику быстро произвести отладку и решить проблемы с лёгкостью недостижимой в других языках.

Часто подвергался критике и синтаксис основанный на отступах, но видя, сколько новых языков внедряют этот подход (на ум приходят HAML, CoffeeScript и многие другие) доказывает, что он получил признание.

Даже тогда, когда я не соглашаюсь с тем, как Реймонд* пишет что-то новое для стандартной библиотеки, качество его модулей не вызывает ни малейшего сомнения и это одна из основных причин, по которым я использую Python. Не могу представить себе работу с Python без доступа к модулю collections или itertools.

Но настоящей причиной, по которой я любил и боготворил Python, было предвкушение каждой новой версии, как у нетерпеливого ребёнка, ждущего Рождество. Мелкие, едва заметные улучшения приводили меня в восторг. Даже возможность указывать начало индекса для функции enumerate вызывали у меня чувство благодарности за новый релиз Python. И всё это с учётом обратной совместимости.

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

Что же изменилось?

Как же получилось, что мне стало не до новых релизов Python? Могу говорить лишь за себя, но я заметил, что и у других изменилось отношение к новым релизам.

Я никогда не задавался вопросами о том, чем занимались разработчики ядра очередного Python 2.x.
Конечно, что-то было не так уж и хорошо продуманно, например реализация абстрактных классов или особенности их семантики. Но в основном всё сводилось к критике высокоуровневого функционала.

С появлением Python 3 появились и внешние факторы, из-за которых мне неожиданно пришлось изменить общий подход к работе с языком. Раньше я долго не использовал новые возможности языка, хоть и был им рад, т.к. в основном писал библиотеки. Было бы ошибкой использовать самое новое и самое лучшее. Код Werkzeug'a до сих пор забит хаками позволяющими ему работать на Python 2.3, хотя сейчас минимальные требования поднялись до версии 2.5. Я оставлял в коде багфиксы для стандартной библиотеки, ведь некоторые производители (печально известная Apple) никогда не обновляют интерпретатор до тех пор, пока в нём не будет найдена критическая уязвимость.

Всё это невозможно с Python 3. С ним всё превращается в разработку для 2.х либо 3.х. И никакого срединного решения не предвидится.

После анонса Python 3, Гвидо всегда восхищенно говорил о 2to3 и том, как она облегчит портирование. А вышло так, что 2to3 это худшее, что могло случиться с Python.

Я испытал огромные трудности при портировании Jinja2 с помощью 2to3, о чём впоследствии сильно сожалел. Более того, в отпочковавшемся проекте JSON Jinja, я убрал все хаки написанные для корректной работы 2to3 и никогда больше не буду его использовать. Как и многие другие, сейчас я вовсю стараюсь поддерживать код работающий как на версиях 2.х так и 3.х. Вы спросите почему? Потому, что 2to3 очень нетороплива, из рук вон плохо интегрируется в процесс тестирования, зависит от версии используемого Python 3 и ко всему прочему настраивается разве что с применением чёрной магии. Это болезненный процесс, сводящий на нет всё удовольствие получаемое от написания библиотек. Я любил обтёсывать Jinja2, но перестал это делать в тот момент, когда порт на Python 3 был готов, т.к. боялся что-либо в нём сломать.

Сейчас же, идея о разделяемой кодовой базе упирается в то, что я должен поддерживать Python вплоть до версии 2.5.

Перемены вызванные Python 3 привели весь наш код в негодность, что никак не служит оправданием для его незамедлительного переписывания и апгрейда. По моему глубоко субъективному мнению, Python 3.3/3.4 должен больше походить на Python 2 а Python 2.8 должен быть ближе к Python 3. Так сложилось, что Python 3 — это XHTML в мире языков программирования. Он не совместим с тем, что пытается заменить, а взамен практически ничего не предлагает кроме того, что он более «правильный».

Немного о Юникоде

Очевидно, что самым большим изменением в Python 3 стала обработка Юникода. Может показаться, что насаживание Юникода всем и каждому — это благо. А ещё, это взгляд на мир сквозь розовые очки, ведь в настоящем мире мы сталкиваемся не только с байтами и Юникодом, но и со строками с известной кодировкой. Хуже всего то, что во многом Python 3 стал этаким Fisher Price* в мире языков программирования. Некоторые возможности были удалены, т.к. разработчики ядра посчитали, что о них можно будет «легко порезаться». И всё это далось ценой изъятия широко используемого функционала.

Вот конкретный пример: операции с кодеками в 3.х на данный момент ограничены преобразованиями Unicode <-> bytes. Никаких bytes <-> bytes или Unicode <-> Unicode. Выглядит разумно, но приглядевшись вы увидите, что сей удалённый функционал как раз то, что жизненно необходимо.

Одним из самых замечательных свойств системы кодеков в Python 2 было то, что она создавалась с прицелом на разнообразную работу с огромным количеством кодировок и алгоритмов. Можно было использовать кодек чтобы кодировать и раскодировать строки, а также у кодека можно было попросить объект, предоставляющий операции на потоках и других неполных данных. A ещё, система кодеков одинаково хорошо работала с контент- и transfer- кодировками. Стоило написать новый кодек и зарегистрировать его, как каждая часть системы узнавала о нём автоматически.

Любой, кто брался за написание HTTP библиотеки на Python, с удовольствием открывал для себя, что кодеки можно использовать не только для декодирования UTF-8 (актуальная кодировка символов), но например и для gzip (алгоритм сжатия). Это относится не только к строкам, но и к генераторам или файловым объектам, если конечно знать, как ими пользоваться.

На настоящий момент, в Python 3, всё это попросту не работает. Они не только удалили эти функии из объекта string, но убрали и кодирование byte -> byte, взамен ничего не оставив. Если я не ошибаюсь, понадобилось 3 года для признания проблемы и начала обсуждения о возвращение вышеперечисленного функционала в 3.3.

Далее, Юникод протолкнули туда, где ему совсем не место. К таким местам относятся слой файловой системы и модуль URL. A ещё, куча Юникод-функционала была написана с точки зрения программиста живущего в 70-х.

Файловые системы UNIX основаны на байтах. Так уж оно устроено и с этим ничего не поделаешь. Естественно, было бы здорово это изменить, что вообще-то невозможно, не сломав существующего кода. A всё потому, что сменa кодировки — лишь малая часть того, что необходимо для Юникод-ориентированной файловой системы. Вдобавок, вопросы форм нормализации и хранения информации о регистре при уже проведённой нормализации остаются открытыми. Останься тип bytestring в Python 3, этих проблем можно было бы избежать. Однако его нет и его замена, тип byte, не ведёт себя так, как себя ведут строки. Он ведёт себя как тип данных, написанный в наказание людям пользующимися байтовыми данными, которые одновременно существуют в виде строки. Не похоже, чтобы он разрабатывался как инструмент, с помощью которого программисты могли бы решить эти проблемы. Проблемы, которые более чем реальны.

Так вот, если вы работаете с файловой системой из Python 3, то странное чувство не будет покидать вас несмотря на наличие новой кодировки с суррогатными парами и экранированием. Это болезненный процесс, болезненный потому, что не существует инструментa для разгребания этого бедлама. Python 3 как-бы обращается к вам, «Приятель, с этого момента у твоей файловой системы Юникод», но при этом не объясняет, с какого конца надо разгребать этот беспорядок. Он даже не проясняет, на самом ли деле файловая система поддерживает Юникод, или же это Python подделывает эту поддержку. Oн не раскрывает подробности о нормализации или о том, как нужно сравнивать имена файлов.

Он работает в лабораторных условиях, но ломается в условиях полевых. Так сложилось, что у моего мака американская раскладка клавиатуры, американская локаль, да практически всё американское, разве что даты и числа форматируются по-другому. В результате всего этого (и как я предполaгаю того, что я проапгрейдил свой мак со времён Tiger'a), у меня возникла следующая ситуация: зайдя на свой удалённый сервер, я получил локаль выставленную в строковое значение «POSIX». Вы спрашиваете, что за «POSIX»? А хрен его знает. Вот и Python будучи в таком же неведении как и я, решил работать с «ANSI_X3.4_1968». В этот памятный день я узнал, что у ASCII есть много имён. Оказалось, что это всего лишь одно из названий ASCII. И вот тебе на, мой удалённый интерпретатор Python криво отобразил содержимое директории с интернациализированными именами файлов. Как они там оказались? Я накидал туда статьи из Википедии с их изначальными названиями. Делал же я это с помощью Python 3.1, который замалчивал происходящее с файлами, вместо того, чтобы выдавать исключения или задействовать какие-либо хаки.

Но неисправности с файловыми системами — это всего лишь цветочки. Python также использует переменные окружения (где как вы знаете, полно мусора) для установки файловой кодировки по-умолчанию. Во время конференции, я попросил парочку посетителей угадать кодировку, использующуюся по-умолчанию для текстовых файлов в Python 3. Более 90% этой маленькой выборки были уверены, что это UTF-8. А вот и нет! Она устанавливается в зависимости от локали платформы. Как я вам и говорил, привет из 70-х.

Смеха ради, я залогинился на оба контролируемых мною сервера и обнаружил, что у одного из них при входе через консоль стоит кодировка latin1, которая переключается на latin15 когда вход осуществляется через ssh под рутом, и UTF-8, если я заходил через своего пользователя. Чертовски занимательно, но винить остаётся лишь самого себя. Не сомневаюсь, что я не единственный, чей сервер волшебным образом переключает кодировки учитывая, что по-умолчанию SSH пересылает настройки локали при логине.

Почему я пишу об этом здесь? Да потому, что снова и снова приходится доказывать, что поддержка Юникода в Python 3 доставляет мне куда больше неприятностей, чем в Python 2.

Кодирование и декодирование Юникода не встаёт на пути у того, кто следует Дзену Python 2 в том, что «явное лучше неявного». «Байты входят, Юникод выходит» — именно так работают куски приложений, которые общаются с другими сервисами. Это можно объяснить. Вы можете объяснить это хорошенько задокументировав. Вы подчеркнёте, что для внутренней обработки текста в виде Юникода есть свои причины. Вы расскажите пользователю о том, что мир вокруг нас суров и основан на байтах, поэтому вам приходится кодировать и декодировать для общения с этим миром. Эта концепция может быть в новинку для пользователя. Но стоит лишь подобрать нужные слова и все хорошенько расписать, как одной головной болью станет меньше.

Почему я говорю об этом с такой уверенностью? Потому, что с 2006 года, все мои программы насаживают пользователям Юникод, a количество запросов касательно Юникода не идет ни в какое сравнение с прорвой запросов о работе с пакетами или системой импортирования. Даже с distutils2, в царстве Python, пакеты остаются гораздо большей проблемой, чем Юникод.

Куда уж не естественное развитие событий: запрятать Юникод подальше от пользователя Python 3. Но обернулось это тем, что людям стало ещё сложнее представлять, как всё это работает. Нужно ли нам априори неявное поведение? Я в этом не уверен.

Несомненно, уже сейчас Python 3 на правильном пути. Я обнаружил, что всё больше разговоров идёт о возвращении некоторых API для работы с байтами. Моей наивной задумкой была идея о третьем типе строки в Python 3, под названием estr, или чего-нибудь в этом роде. Он работал бы точно так, как str в Python 2, хранил бы байты и имел такой же набор строковых API. Однако в нём бы также содержалась информация о кодировке, которая бы использовалась для прозрачного декодирования в Юникод-строку или приведения к байтовому объекту. Этот тип был бы священным граалем могущим облегчить портирование.

Но его нет, а интерпретатор Python не разрабатывался с заделом на ещё один тип строки.

Мы разрушили их Мир

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

Однако, главные изменения коснулись образа мышления, который нужен при работе на этих уровнях. В Python 2 очень часто использовались объекты Unicode для общения с нижними уровнями. При необходимости, объекты кодировались в байты и наоборот. Приятным для нас побочным эффектом, была например возможность ускорить некоторые операции, кодируя и декодируя данные на ранних стадиях и передавая их в канал понимающий Юникод. Во многом это позволяет функционировать модулю сериализации в ядре. К примеру, Pickle общается с потоками поддерживающими как байты, так и Юникод. В какой-то степени то же самое можно сказать и о simplejson. И вот, появляется Python 3, в котором внезапно нужно разделять Unicode и байтовые потоки. Многие API не выживут на пути к Python 3, без кардинальных изменений в их интерфейсах.

Да, это более правильный подход, но на деле у него больше нет никаких достоинств, кроме того что он более правильный.

Работая с функционалом ввода/вывода в Python 3, я убедился, что он великолепен. Но в реальности, он не идет ни в какое сравнение с тем, как работал Python 2. Может показаться, что у меня много предубеждений, ведь я так много работал с Python 2 и так мало с Python 3, однако, написание большего количества кода для достижения одинакового фунционала, считается дурным тоном. А с Python 3, мне приходиться всё это делать, учитывая все его аспекты.

Но ведь портирование работает!

Конечно же, портирование на Python 3 работает. Это было доказано, и не один раз. Но только потому что что-то возможно и проходит все тесты не значит, что всё хорошо сделано. Я — человек с недостатками и совершаю кучу ошибок. В то же время, я горжусь стремлением довести до блеска свои любимые API. Иногда я ловлю себя на том, что снова и снова переписываю кусок кода, чтобы он стал более удобным для пользователя. При работе с Flask я столько времени потратил на оттачивание основного функционала, что самое время начать говорить об одержимости.

Я хочу, чтобы он работал идеально. Когда я использую API для обычной задачи, мне хочется, чтобы они имели такой же уровень совершенства, каков присущ дизайну Porshe. Да, это — лишь внешний слой для разработчика, но продукт должен быть хорошо разработан от начала и до конца.

Я могу заставить свой код «работать» на Python 3 и всё равно я буду его ненавидеть. Я хочу, чтобы он работал. Но при этом, используя свои или чужие библиотеки, мне хочется получать такое же удовольствие с Python 3, какое я получаю от Python 2.

Jinja2, например, некорректно использует слой ввода/вывода в Python 3, так как невозможно использовать один и тот же код на 2.x и 3.x без переключения между реализацией во время исполнения. Теперь, шаблоны открываются в бинарном режиме как в 2.х, так и в 3.х, т.к. это — единственный надёжный подход, а после, Jinja2 сама декодирует данные из этого бинарного потока. Вообще-то это работает, спасибо нормализации разделителей новых строк. Но я более чем уверен, что все, кто работает в Windows и самостоятельно не нормализует разделители строк, рано или поздно попадут в ситуацию с месивом из различных разделителей, совершенно об этом не подозревая.

Принимая Python 3

Python 3 многое изменил, это факт. Без сомнения, за ним будущее в которое мы направляемся. Многое в Python 3 подаёт большие надежды: значительно улучшенная система импортирования, появление __qualname__, новый способ распространения пакетов Python, унифицированное представление строк в памяти.

Но в настоящее время, портирование библиотеки на Python 3 выглядит как разработка библиотеки на Python 2 и создание её (прошу прощения за мой французский) хитрожопой версии для Python 3 лишь бы доказать, что она работает. Про Jinja2 на Python 3 можно во всех отношениях сказать, что она чертовски уродлива. Это ужасно и мне должно быть стыдно за это. Например, в версии для Python 3, Jinja2 загружала два одномегабайтовых регулярных выражения в память, и я совершенно не заботился о её освобождении. Мне просто хотелось, чтобы она хоть как-нибудь работала.

Так почему же мне пришлось использовать мегабайтовые регулярные выражения в Jinja2? Да потому, что движок регулярных выражений в Python не поддерживает Unicode-категории. А с такими ограничениями пришлось выбирать меньшее зло из двух: либо забить на новые Unicode-идентификаторы в Python 3 и ограничиться идентификаторами ASCII, либо создать огромное регулярное выражение вручную, вписав в него все необходимые определения.

Сказанное выше — лучший пример объясняющий, почему я пока не готов к Python 3. Он не предоставляет инструменты для работы с его же нововведениями. Python 3 жизненно необходимы Unicode-ориентированные регулярные выражения, ему нужны API для работы с локалями, учитывающими взятый курс на Unicode. Ему нужен более продвинутый модуль path, раскрывающий поведение нижележащей файловой системы. Он должен сильнее насаждать единую стандартную кодировку для текстовых файлов, не зависящую от среды исполнения. Он должен предоставлять больше инструментов для работы с закодированными строками. Ему нужна поддержка IRI, а не только URL. Ему нужно что-то большее чем «yield from». В нём должны быть вспомогательные механизмы для транскодирования, которые необходимы для отображения URL в файловую систему.

Ко всему вышеперечисленному можно добавить выпуск Python 2.8, который бы был чуть ближе к Python 3. По мне, существует лишь один реалистичный способ перехода на Python 3: библиотеки и программы должны быть полностью осведомлены о Юникоде и интегрированы в новую экосистему Python 3.

Не пускайте дилетантов прокладывать ваш Путь

Самой большой ошибкой Python 3 является его бинарная несовместимость с Python 2. Тут я подразумеваю отсутствие возможности совместной работы интерпретаторов Python 2 и Python 3 в пространстве общего процесса. В результате, вы не можете запустить Gimp одновременно со скриптовыми интерфейсами как Python 2 так и Python 3. То же самое относится к vim и Blender. Мы просто-напросто не можем. Не сложно понаписать кучу хаков с отдельными процессами и вычурным IPC, но это никому не нужно.

Таким образом, программист, которому придётся раньше других осваивать Python 3, будет делать это из-под палки. И не факт, что этот программист вообще хорошо знаком с Python. А причина, положа руку на сердце, в том, что деньги крутятся вокруг Python 2. Даже если по ночам тратить все наши силы на Python 3, то днём мы всё-равно будем возвращаться к Python 2. Так будет до поры, до времени. Однако, если кучка графических дизайнеров начнёт писать скрипты на Blender под Python 3, то вот вам и нужная адоптация.

Мне совсем не хочется видеть kak CheeseShop* будет мучаться от обилия кривых портов библиотек на Python 3. Мне совсем не хочется видеть ещё одну Jinja2 и особенно уродливую кучу кода призванного работать и на 2.х, и на 3.х. Туда же, хаки вроде sys.exc_info()[1], для обхода синтактических различий, хаки конвертирования литералов во время исполнения для совместимости с 2.х и 3.х и многое многое другое. Всё это плохо отражается не только на производительности во время исполнения, но и на основных постулатax Python: красивый и разборчивый код без хаков.

Признать неудачи, Учиться и Приспосабливаться

Сейчас самое время для нас собраться и обсудить всё то, что люди делают для работы их кода на 2.x и 3.х. Технологии эволюционируют быстрыми темпами и мне будет очень обидно наблюдать, как Python разваливается только потому, что кто-то упустил из виду тёмные тучи на горизонте.

Python не «слишком велик, чтобы о нём забыли». Он может очень быстро потерять свою популярность. Pascal и Delphi попали в узкую нишу, несмотря на то, что они оставались восхитительными языками даже после появления на свет C# и .NET framework. Больше всего на их падении сказался неправильный менеджмент. Люди до сих пор разрабатывают на Pascal, но много ли тех, кто начинает писать на нём новые проекты? Deplhi не работает на iPhone и Android. Он не очень-то хорошо интегрирован в рынок UNIX. И если быть честными, в некоторых областях Python уже сдаёт позиции. Python был достаточно популярен в области компьютерных игр, но этот поезд уже давно ушёл. В web-сообществе, новые конкуренты появляются как грибы после дождя, и нравится это нам или нет, JavaScript всё чаще и чаще наступает на позиции Python как скриптового языка программирования.

Delphi не смог вовремя приспособиться и народ просто перешёл на другие технологии. Если 2to3 это наш путь перехода на Python 3, то py2js — это путь перехода на JavaScript.

И вот что я предлагаю: могли бы мы составить список всего, что усложняет переход на Python 3 и список решений, позволяющих эти проблемы решить? Могли бы мы заново обсудить разработку Python 2.8, если он сможет помочь с портированием? Могли бы мы признать PyPy действительной имплементацией Python, достаточно весомой, чтобы повлиять на то, как мы пишем код?

Armin Ronacher,
7 Декабря 2011.

От переводчика: Прочитав эту статью, первым же желанием было поделиться с другими, возникло острое ощущение, что «мир должен знать». Пересказать статью помогали моя коллега Ирина Пирогова и моя жена Айла Мехтиева, за что им огромное спасибо!
Zaur Nasibov @BasicWolf
карма
46,2
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (120)

  • +3
    Мне кажется, автор таки перегибает палку местами. Unicode в именах файлов должен быть. Кодировка по умолчанию и в самом деле должна определяться из системной локали — а почему, собственно, должно быть по-другому, ведь зачем она тогда? Многие другие языковые рантаймы так делают уже давно, и вроде бы всех это устраивает.
    • 0
      P.S. Вышесказанное не значит, что я против Python 2, Python 3 или чего-либо ещё.
    • +1
      кодировка работы с конослью — да, берите из LANG и LC_*
      Но если я открыл файл на чтение — почему ЭТО должно зависеть от локали?
      Я блин свой конфиг не смогу прочитать если зайдёт пользователь с другой локалью!
      • 0
        ./somescript <text.txt — какая должна быть кодировка? С одной стороны это обычный файл, с другой консоль.
        • 0
          не путайте STDIN и консоль!!!
          • 0
            Так я привёл пример, когда это не одно и то же.
      • 0
        Ну так скажите, что вам нужно строго UTF-8, если это так важно. Проблемы-то какие?
      • 0
        Конфиг не Ваш, а пользовательский. Пользователь решил, в какой кодировке его хранить, а Вам сказал это через LC_*.
        • +2
          А если сисадмин и пользователь пользуются разной локалью?
          • 0
            «Пользователь» в данном контексте — «пользователь программы». Администратор — тоже пользователь.
            • +2
              Пример. Администратор пишет конфиг в /etc/someapp/someapp.conf, пользуется там русскими буквами. А читается он пользователем, у которого другая локаль.
              • 0
                Чтоб конкретнее было. Администратор олдскульный и пользуется ru_RU.KOI8-R, а пользователь ru_RU.UTF-8 по умолчанию.
                • +1
                  Гнать такого админа въетнамским веником!
        • +2
          Хм… Установка программы глобально (в /usr/… например, глобальные конфиги в /etc/… соответственно), один пользователь с ja_JP.EUC-JP, другой с ru_RU.KOI8-R. Что-то мне подсказывает, что проблемы будут у обоих при чтении конфига.
          • 0
            Подавляющее большинство программ позволяют оверрайдить system-wide конфиги юзерскими.
            • 0
              Позволяют, конечно, но далеко не все пользователи этим пользуются, да и первый старт всё равно с глобальными пойдёт.
    • 0
      С одной стороны, да, должна определяться из локали. Хотя бы для корректного вывода в консоль (Windows уже перешла на Unicode в консоли?). С другой — записывая файл на внешний носитель я, наверное, хочу чтобы он прочитался на другой машине, на которой локаль может быть совсем другая и уж точно не хочу потери информации при записи. Стандартом де-факто для такой записи является одна из кодировок Unicode (UTF-8 в подавляющем большинстве случаев). Универсального решения в голову не приходит, разве что сделать по умолчанию UTF-8, а для stdin/stdout/stderr писать в кодировке локали. Но костыль явный.
      • 0
        Нет же. Дать вменяемый API. Нужно Вам ISO-8859-1 — сказали [fconfigure $f -encoding iso-8859-1]. Нужно UTF-8 — [fconfigure $f -encoding UTF-8]. Нужно, чтобы пользователь сам определил — ничего не указывайте. Нужно байтами пожонглировать — [fconfigure $f -encoding binary]
        • +1
          Что вменяемый API нужен это понятно. Вопрос в умолчании. Почему пользователь, а не общее для всех скриптов умолчание? А хочешь учесть пользовательские хотелки — [fconfigure $f -encoding locale].
          • 0
            Так умолчание пользователь задаёт. Локалью. Она ж для того и предусмотрена.
            • +1
              Некоторые пользователи слов таких не знают… И даже на одной машине к общим файлам могут обращаться пользователи с разной локалью.
              • 0
                Если не знают — ну будут использовать локаль по умолчанию, тем более.
                • 0
                  Не знаю, по мне так лучше бы было чтобы кодировка по умолчанию была одна во всех возможных средах. Решил пользователь сменить локаль (или за него решили) и у него файлы перестали читаться.
                  • 0
                    Я ж говорю: если какие-то конкретные файлы нужно читать с другой кодировкой, программа явно её запросит (а её об этом, в свою очередь, попросит юзер, если захочет). А пока не запросит — дефолтом должна быть та кодировка, которой пользуется пользователь, а не какая-то там UTF-8.
                    Ну вот стоит у меня, к примеру, какая-нибудь EBCDIC по умолчанию. А программа XYZ требует (!) конфиг в UTF-8. Какого-такого? Мне ради этой программы в редакторе переключать кодировку? Да ни разу, она должна сразу брать конфиг в моём любимом EBCDIC.
                    • –2
                      Не согласен.
                      • 0
                        Поясните.
                        • 0
                          Просто мнение, что файлы на диске, при передаче по сети и т. п., должны храниться в единой кодировке. Грубо говоря, скопировали вы свой конфиг в EBCDIC на флэшку и пришли ко мне поработать, а у меня UTF-8. Что будете делать?

                          Мнение сформировалось давно, самое позднее в эпоху перехода от CP866 к Windows-1251.
                          • +1
                            Нет единой кодировки. Просто не существует. Unicode не покрывает множество всех возможных символов. Не говоря уже о том, что собственно юникодных кодировок выше крыши.
                            • 0
                              Ну Unicode, афаик, наиболее близок к этому идеалу и ещё имеет резерв на расширение, не ломающий (почти) обратную совместимость.
                            • 0
                              Вот это верно, юникодных слишком много разновидностей.
                          • 0
                            А что буду делать — у Вас перекодирую. Или Вы перекодируете. В зависимости от того, где и кому нужно. Но у себя я буду хранить в EBCDIC, причём буду ожидать от программ, чтобы они работали сразу в ней без дополнительных телодвижений. На то настройки локали и нужны, чтобы указывать, в какой кодировке работать. (Вообще странно, мне казалось, что это должно быть очевидно, но, видимо, не всегда и не всем.)
                            • +1
                              Вы правы, не всем. Настройки локали в плане кодировок считаю костылём, обусловенным историческими причинами.
                    • 0
                      Как-то вы не о том рассуждаете, не логично получается. Как бы надо отличать для чего предназначен файл. Вы же ко мне не придете с конфигом microsoft word, так? Вы придете со своим юзерским файлом file.txt. То же самое должно быть и у программы на python.

                      Те данные, которые определяют настройки программы должны быть записаны в определенный файл в кодировке, например, utf-8 и не должны содержать юзерских данных в его кодировке (раз этот файл настроек един для всех юзеров системы).

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

                      Коротко: конфиг общий — UTF-8, конфиг юзера — его кодировка (EBCDIC).
    • +1
      Господи, 21й век, а все еще обсуждают эти кодировки. Всё кроме юникода должно отправиться к праотцам, туда же, куда отправились четырехкилобайтные машины, неужели это неочевидно?
      • 0
        Расскажите это китайцам и японцам, а потом мы все вместе посмеёмся.
        • +1
          А что там не так? Погуглил, не нагуглил.
          В юникоде нет части китайского-японского, и не осталось свободных мест?
          Ну значит и он должен отправиться к праотцам.

          Времена четырехкилобайтных машин прошли. На текст можно тратить сколько угодно места.
          • 0
            Во-первых, не прошли, а во-вторых, узнайте у тех и других, насколько их устраивает Юникод. Будете удивлены.
            • 0
              Так вы, кажется, знаете, чем он их не устраивает. Может расскажете? Не знаком ни с одним китайцем чтобы спросить :(
              • –1
                Краткий ответ я уже здесь писал, а за подробностями отправляйтесь в гугль, там Вам лучше расскажут, чем я.
          • +1
            Да, и не забывайте про:
  • 0
    Хорошая статья, спасибо за перевод.
  • 0
    Вообще сложно себе представить как это всё происходит. Мелкие скрипты написать так, что бы работали и под python2 и под python3 не проблема, но вот портировать библиотеки… Наверное это действительно огромная головная боль. Плюс ничего не сказано про C-extensions — они остались вполне совместимы между Py2 and Py3?
    • 0
      C-extension'ам ещё больше досталось, т.к. очень много изменений в API. Причём есть такие мелкие моменты, что и не обратишь внимание:

      Было:
      static PyTypeObject mytype = { PyObject_HEAD_INIT(NULL) 0, ... };

      Стало:
      static PyTypeObject mytype = { PyVarObject_HEAD_INIT(NULL, 0) ... };
      • 0
        Я конечно считаю, что обратную совместимость часто можно выкинуть, но блин они либо не должны затрагивать разработчиков тысяч библиотек, либо должны приносить какую-то реальную и большую пользу.

        Вот что полезного дало изменения которое вы показали? Наверное стало что-то «очевиднее», но совсем чуть-чуть.
        • 0
          они = «изменения ломающие обратную совместимость»
        • 0
          Кстати, вот интересная вещь. Есть такой язык, Tcl. Так вот, несмотря на то, что он постоянно развивается, API меняются таким образом, что минимально ломают обратную совместимость, а чаще всего не ломают совсем. Причём, как на уровне Tcl, так и на уровне C. С большой вероятностью C-расширение, написанное для бородатой версии Tcl, можно с полпинка завести на свежем срезе development-версии. Если же речь идёт о коде на Tcl, то 90% кода работать будет просто сразу, без модификаций, только быстрее :)
          • 0
            З.Ы. Tcl вспоминаю не холивора ради, а просто для того, чтобы показать, как бывает по-другому.
        • 0
          К сожалению с C-extensions имел очень мало дела на Python 3, но в данном примере — изменения описанные в PEP 3123, связанные с С-шным «strict aliasing rules». А вообще на практике очень многое решается с помощью макросов.
          • 0
            Ну я вообще работал только с cython и то просто побаловаться и сравнить. Пилил какую-то pygame штуку и привязывал к ней cython математику, собирался сравнить производительность — но так руки и не добрались.

            offtop:
            как там, кстати с играми на питоне? PyGlet вижу давно не обновлялся. Pygame вроде не плох, но все готовые либы для ускорения ччерез opengl как правило ломают стандартные спрайты и вводят свои. И когда я последний раз проверял такие либы, то все они были или бородатые или бесполезные.

            Хочу rapid game development with python с opengl, коллизиями и физикой из коробки! Плюс много сторонних хороших либ для side-scrolling, изометрии, скролинга больших миров и т.д. Из последнего — почти всё есть в pygame, но порой трудно найти.
            • 0
              посмотрел новости pygame: на GSoC нынче три проекта — удобный гуи, улучшение спрайтов и сцен, и лёгкиё networking. В целом наверное неплохо, да и видно, что зазвитие идёт. Есть ли «крутые» долгоиграюзие проекты на pygame?
        • +1
          Ну в Python 3 как раз и шли к максимальной очевидности.

          Мне кажется, лучше переболеть один раз, зато избавиться от всех legacy проблем.
          • +1
            Согласен. Только проблема в том, что можно не вылечиться… к сожалению, болезни иногда переходят в хроническую стадию…
        • 0
          Обратную совместимость питона итак сломали, поэтому кромсали апи на всю катушку — такая возможность еще не скоро представится :)
  • +5
    Десять лет моя жизнь проходит вместе с Python. И пока это бОльшая часть моей жизни.

    Правильно ли я понимаю, что Армину 19 лет и python он использует с девяти?
    • 0
      Ему 23 :) Думаю он имел ввиду «сознательную» часть жизни. Вот его профиль на stackoverflow.
  • –19
    Лично я не буду сильно переживать, если доля питона уменьшится. Уж слишком тормозные вещи на нем выходят.

    PS Ну и замены Scheme на Python, в базовых курсах MIT, никогда не прощу.
    • –6
      Минусующие комрады, вы хоть скажите с чем не согласны? Или у меня одного запуск synaptic занимает неприлично много времени?
      • +7
        incogn1too, я лично не минусовал, но фраза «уж слишком тормозные вещи на нём выходят» без приведения конкретных примеров (да и сама по себе) — показывает твою некомпетентность в данной области.
        • –1
          Да я вроде указал пример — synaptic. Установлен почти во всех Linux дистрибутивах.
          • 0
            Что делать синаптику «почти на всех линукс дистрибутивах»?
            И тормозит он совсем не из-за питона, тем более, что написан на С.
      • 0
        Могу предположить за пренебрежительное отношение к любимому языку минусующих :) А так, по сути, а кто ему на замену придёт? Ruby? JavaScript? прости господи, PHP?
      • +1
        Комрады, тормозные вещи на питоне, Scheme. Я думаю ты и двух заданий из SICP не сделал.
        • –3
          Так думать — ваше право, но думы не всегда соотвецтвуют действительности.
          • –1
            Орфография.
            • –1
              Благодарю.
          • –1
            * соответствуют
      • +1
        Не нравится — напишите свой synaptic на С.
        Я считаю, Python вообще не для десктопных аппликаций. В вебе на нем писать легко и приятно, что доказывает огромное кол-во популярных фреймворков (начиная от Tornado, кончая Django и Flask). Замен для себя не вижу — Ruby медленнее (+ учить новый синтаксис, стандарты и тд. — не хочу), JS — пока несерьезно (нет даже неймспейсов — говорить не о чем), Java — на всяких JSF и Spring кол-во кода не идет ни в какое сравнение с тем-же Flask, C# — хоть и по работе занимаюсь с ASP.NET, для своих проектов windows-хостинг заводить не собираюсь (и выкладывать килобаксы за всякие TFS), ASP.NET MVC на Mono выглядит сыро.
        • 0
          В js (серверном, node.js/common js имею в виду), кстати, отличные неймспейсы через require, очень похоже на питоньи импорты, только магии меньше)
          • 0
            да в любом JS есть пространства имен.

            (function() {
             ...
            )();
            
  • +5
    у блоков Ruby восхитительный дизайн, но по многим причинам он не прижился бы в Python (в его текущем состоянии).

    Вот про это было бы интересно почитать…
  • –1
    По файлам не согласен.
    А так подо всем подпишусь.
    Некоторые вопросы в Python 3 сделаны правильно, но они сделаны правильно в ущерб простоте. А конек Питона в простоте.
    Наверное самая большая проблема Python 3 в том, что он ничего не принес нового, кроме проблем и головной боли. Требовалось избавиться от GIL. Чтобы получить быстроту с работающим кодом Python 2 с некоторыми возможными изменениями.
    А изменения ради самих изменений ни к чему хорошему не приводят.
    Раньше во времена одноядерников Python развивался, сейчас он стоит. Потому что не сделал шаг в сторону девелоперов, не решили проблемы с GIL и простым общением тредов/процессов.
    • +1
      Всё-таки GIL это часть CPython. Гвидо не раз указывал, что GIL остаётся в его реализации, чтобы не усложнять код. Есть же PyPy, или расширения вроде gevent.
      • +1
        Да, но ни PyPy, ни gevent не решают проблему GIL. Одновременное исполнение нескольких потоков с вычислительными операциями. Единственная попытка решить в лоб — это multiprocessing. А так есть способ писать эффективный код путем смены логики, но не редко за логикой идет и порядочное усложнение кода. Так что «не усложнять» не получается. Раз уж питон все равно обратно не совместим со второй веткой? можно было придумать что-то более эффективное пусть и сложнее. Например в Gо очень хитро придумали с машинами и многопоточностью.
        • 0
          Go всё-таки компилируемый язык со статической типиазацией изначально ориентированный на параллельные вычисления. Мне кажется, что ошибочно сравнивать эти языки.
          • +1
            Я ни в коем случае не сравнивал, просто указал как пример неплохой идеи по многопоточной обработке. Естественно для python такая модель не подойдет, но пока что люди не видят реальных продвижений в эту сторону.
      • 0
        Костыли, опять же.

        То есть решения на уровне C нет.
  • –2
    Я свой веб-фрэймворк и шаблонизотор ене написал (пока), но о строках и кодировках успел составить противоположное мнение. С третьим Питоном функция, принимающая строку, ругается на байты и наоборт. Во втором же неявные decode('ascii')/encode('ascii') меня всегда запутывали. Кому вообще нужна кодировка ascii? Приходилось постоянно подглядывать в документацию, чтоб вспомнить, что же именно возвращает очередной метод — чистую строку или в какой-то кодировке. В простых случаях приходилось использовать костыль sys.setdefaultencoding('UTF-8').
    • +2
      Вы не поверите, но ASCII таки нужна…
      • 0
        Верю. Какой-нибудь URL прочитать, например. Это было маленькое эмоциональное преувеличение.
        • 0
          Какой-нибудь URL прочитать, например

          Например какой? президент.рф/? Или ta.wikipedia.org/wiki/முதற்_பக்கம்?
          • 0
            Нет, нормализованный. Такие, конечно, нужно читать как текст.
      • 0
        Вы не поверите, но ASCII таки нужна…

        Зачем? Чтобы экономить на размере русских текстов?
        • 0
          Как связаны ASCII и русские тексты? КОИ-7?
          • 0
            Ну, если подходить с этой стороны, «де-юро», то КОИ-7 к ASCII как раз не относится, зато относятся CP-866 и ISO-8859-5. Русский я взял как более близкий местной аудитории и тот факт, что русские ASCII кодировки практически полностью уступили однобайтовой же ANSI — это лишь такой исторический курьёз. Вместо русского можно взять, к примеру, чешский, где едва ли ни основной (на ряду с ANSI CP-1250) однобайтовой кодировкой всё-ещё является ASCII ISO-8859-2 (если мне не изменяет память, именно она на чешских системах ставится кодировкой по умолчанию в почтовиках типа Outlook, Outlook Express, Windows Mail и Thunderbird).

            Если Вы имеете ввиду нижнюю половину, чисто английские тексты, то я вообще не понимаю о чём тут дискутировать, ведь эти символы, на сколько я понимаю, кодируются в UTF-8 идентично ASCII и разницы тут между ними вроде бы нет.

            Я говорил о том, что единственный (кроме относительной сложности реализации) известный мне аргумент против UTF-8 (в пользу однобайтовых ASCII/ANSI/KOI) — что текст на языке с интенсивным использованием специфических символов (в частности язык с не-латинской графикой) в однобайтовой кодировке занимает меньше места, чем в UTF-8. Кроме желания сэкономить на этом, не вижу причин использовать кодировки отличные от UTF-8 (можно, конечно, ещё вспомнить об UTF-16 и UTF-32 но, на сколько я понимаю (поправьте если ошибаюсь), по сравнению с UTF-8 они просто устарели).

            Понимаю, что рискую прослыть максималистом и вызывать справедливое негодование многих, но считаю, что в современном языке программирования стандартным представлением любой строки (и без всяких костылей) должно быть UTF-8 (на крайняк UTF/UCS-чего-нибудь-ещё). При чтении текста из внешнего потока входная кодировка должна либо (в случае умолчания) браться из локали (на сколько я понимаю, тут так и сделано, что и вызвало негодование автора), либо не браться и считаться априори (спорно) UTF-8, либо указываться программистом явно (надеюсь такая возможность есть, иначе действительно фигня).

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

            Если я не прав — буду признателен на указания в чём и почему (кроме соображений экономии — о них, как я уже указал, мне известно).
            • 0
              UTF-16 и UTF-32 не устарели. У них просто немного другая ниша — быстрая обработка данных.
              Вот представь, что тебе нужно изменить один символ в UTF-8 и новый символ имеет другую байтовую длину. Получается, что нужно перестроить весь массив после этого символа, что довольно затратно. Плюс к этому, размер данных нельзя посчитать исходя из количества символов (бывает важно для низкоуровневых языков).
              Или посмотри на алгоритм чтения символов из UTF-8… Нельзя просто брать нужное количество байт и не париться.
              Поэтому, например, Qt используют UTF-16 для своего QString.
  • +7
    Проблема добавления поддержки 3го питона в библиотеки есть, но не так уж все и плохо. Новые библиотеки достаточно просто писать так, чтоб они поддерживали и 2.х, и 3.х без всяких утилит 2to3.

    «Хаки вроде sys.exc_info()[1], для обхода синтактических различий, хаки конвертирования литералов во время исполнения для совместимости с 2.х и 3.x» — эти хаки не нужны, если не поддерживать 2.5. А его поддерживать imho не нужно, т.к. в него даже security-обновлений не будет больше, да и в хоть как-то актуальных ОС его нет (в предыдущем долгоиграющем red hat 2.4, а не 2.5, и это совсем другая история).

    Очень многие «большие» старые библиотеки уже портированы (вот это бывает трудно, да). C-расширения можно писать не напрямую, а через ctypes, cython и тд — поддержка 3го питона тогда в них появляется более-менее автоматически. Писать C-расширения на C все равно не очень хорошо, т.к. под pypy они если и заведутся, то по-тормозному.

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

    Я сейчас посчитал, у меня 8 штук своих open-source библиотек, которые под 2.х и 3.х работают из одного исходного кода (втч с C расширениями), + несколько чужих библиотек портировал (или помогал портировать). Писать новый код, который и там, и там работает — совсем не сложно, это просто дело привычки, и выглядит он ничем не хуже, чем код для 2.х (а часто даже и лучше, т.к. приучаешься к __future__ импортам и более новым языковым конструкциям). Портировать — сложнее, особенно большие, старые или трюкачистые, но тоже вполне реально (nltk так и не допортировал окончательно, но там большая часть тестов уже проходит, и пара глав из nltk book работает, жду помощи из-за границы теперь).
    • +2
      Кстати, какие реальные кейсы написания нового кода для 2х и 3х одновременно? Почему бы не писать сразу под 3х?
      • 0
        Я такой кейс встретил, когда разбирал работу ObjectID в MongoDB'шном BSON (имею ввиду биндинги для питона). Уж очень напоминает #ifdef WINDOWS ... и похожую лабуду.
        • 0
          Это смотря как писать. if PY3:… else:… — это дурной тон и почти никогда не нужно. Без ветвления 2-3 часто можно обойтись, а когда нельзя — оно выносится в отдельный модуль (типа compat.py) и не засоряет код.

          pymongo (о нем ведь речь) еще недавно поддерживал (о ужас!) python 2.3, ясно-понятно там куча хаков будет даже без поддержки 3го питона. 2.4-то поддерживать невесело. По-моему писать код, совместимый с 2.3 — 2.7 посложнее будет, чем 2.6 — 3.2, и сам код жутковатым будет, в отличие от варианта 2.6-3.2.
      • +1
        Сейчас все еще бОльшая часть нового кода — под 2й питон пишется, и поддержка библиотекой 2го питона — большой плюс.
  • +1
    Спасибо за перевод и публикацию! Всё, что разрабатывает Армин отличается редкой элегантностью и продуманностью. Взять хотя бы его flask и Jinja2. Поэтому многое, что он пишет в своем блоге заслуживает внимательного прочтения и дальнейшего обдумывания.

    PS: Только не понятно, почему вы сократили его фамилию(?) Фамилия австрийская и читается Ронахер. С ударением на первый слог.
    • 0
      Просто казалось, что «Ronacher» будет читаться как «Ронаха» по аналогии с напр. «Arbeiter» -> «Арбайта», где окончание сглатывается за счёт нисходящей интонации.

      (P.S. это воспоминания из школьного немецкого, поправьте пожалуйста, если не прав).
      • 0
        :) это вы перегибаете. тогда уж «Ронахэ».

        Но мы же говорим «Ганновер», а не «Ханофэ» просто потому что есть определенные традиции транскрибирования.
        • +1
          Спасибо за разъяснение. Изменил.
  • 0
    Мелкий нюанс перевода:
    «Я насчитал сотню экземпляров одной заглавной буквы в этом тексте.»

    В русском языке «я» далеко не всегда пишется с заглавной буквы, в отличие от английского. Поэтому людям незнакомым с английским фраза вероятно будет непонятна.

    В целом же перевод хороший. Автору большое спасибо.
  • +1
    При всё моём уважении к Армину и его библиотекам/флеймвокам, он, всё-таки, истеричка. Нет, я совершенно не хочу его унизить, оскорбить или обидеть. Просто он очень испугался нововведений и тому, что нужно поддерживать две ветки своих проектов.
    Его PEP-0414 вообще бредовый — ну зачем нужны эти атавизмы?
    Даже не знаю, что ещё сказать… Например, имена файлов в py2 я уже лет пять, как пишу в юникоде и не вижу вообще никаких проблем. Зависимость от локали, это совершенно правильно — настраивайте свою локаль так, чтобы не было неоднозначностей.
    Поддерживать 2.5… Да я и 2.6 предпочитаю не поддерживать, если для этого нужно большее, чем поставить индексы при форматировании строк (u'{0}-{1}'.format()).

    В общем, делайте свои выводы из написаного, а не берите на веру.

    P.S. Читал ещё полгода назад…
    • 0
      Можно пожалуйста пример Ваших проектов, или хотя бы приблизительное сравнение по размеру пользовательской базы с проектами Армина? Это к поддержке старых версий Python.
      И при чём тут имена файлов в Py2? Он про работу фс в Py3 писал.
      • –1
        Вот находятся же умники… Ещё раз перечитай статью, потом мой комментарий и подумай, что ты спросил не так.
    • 0
      Мне понравился термин «флеймвок», надо взять на вооружение.
  • 0
    Вот находятся же умники… Ещё раз перечитай статью, потом мой комментарий и подумай, что ты спросил не так.
    • 0
      Не туда, сорри.
  • 0
    Ну и что плохого в том, что строка — это набор символов Юникод, а не байтов?
    Массив байтов теперь правильно называется, и прекрасно декодируется в Юникод и обратно при указании локали.
    Большинство библиотек работающих с пересылкой данных по сети требуют кроме 2to3 ещё и дополнительных правок с encode() и decode() и это здорово.
    Теперь мы пересылаем по сети массивы байт, а не строки, как и было задумано.
    Наконец-то это древний допотопный анахронизм про то что байт = символ уходит в прошлое. Мне не нужно думать о том что такое символ, не нужно думать о кодировке, у меня есть строка и я всегда уверен что она однозначно определена.
    Большое спасибо за перевод, статья интересная, хоть и излишне экспрессивная.
    • 0
      URL это строка байтов или символов?
      • 0
        Байтов. Не ascii преобразовываются пуникодом.
      • 0
        Вообще-то обычная строка, либо Request:
        urllib.request.urlopen(url, data=None[, timeout], *, cafile=None, capath=None)
        Open the URL url, which can be either a string or a Request object.
        docs.python.org/py3k/library/urllib.request.html
        И это — логично!
        • 0
          С точки зрения питона да, всё верно.
        • 0
          > И это — логично!

          а теперь прикиньте во что превращается аккуратное:
          url = "http://%s:%d/" % (host, port)
          в p3k.

          зы: не троллинга ради — это Армин так писал lucumr.pocoo.org/2010/5/25/wsgi-on-python-3/ :)
          • 0
            Если host — строка, то ни во что криминальное не превращается. Всё как и раньше.
            Если host — массив байт, то что мы называем «строкой» в С++ или Python 2.x, то нужно писать так:

            url = «%s:%d/» % (host.decode('cp1251'), port)

            Вот именно из-за аргумента в методе decode() и нельзя считать массив байт строкой. Поскольку в строке каждый элемент — символ, а в массиве байт — каждый элемент байт. Если нужно массив байт однозначно представить строкой — используйте decode(encoding).

            Интерпретация чего бы то ни было строкой должна быть явной, либо вы используете __str__.
            См. import this — явное лучше неявного. Не уверен в "%s" % X — не пиши.
            • 0
              Собственно, согласен.

              P.S. Кстати, вчера случайно выяснили пренеприятную вещь: urllib2 не знает, что такое пуникод и преобразование надо делать самим. :-(

              P.P.S. И ещё момент… Пишите уже современно:
              url = "http://{:s}:{:d}/".format(host, port)
              Это куда красивее!
              • 0
                А почему не просто {} вместо {:s}?
                • 0
                  Как-то автоматически. На самом деле я бы написал просто {}. :-)
              • +1
                В ряде случаем, имхо, симпатичнее выглядит старое форматирование:
                'Введите значения поля "%s": ' % field_name
                Не нужно писать скобки (для одного аргумента), вызывать метод format (опять же со скобками), и мы сразу же принудительно указываем, что выводим строку.
                Хотя я конечно фанатею от конструкции
                '{:%Y-%d-%m %H:%M:%S}'.format( datetime.now() )
                • 0
                  И всё-таки тут я не соглашусь. Мне всегда эти проценты дикостью казались и я был рад от них избавится, даже ценой нескольких символов.

                  Андрей Светлов, кажется, писал про возможности расширения этого форматироватья. Пример с датой, это ещё цветочки…
              • 0
                мм… не так. если url это массив байтов, значит надо использовать b"":

                url = b"http://{:s}:{:d}/".format(host, port)

                (если что, это не работает)
                • 0
                  Я просто не сразу уловил твой вопрос.

                  В общем случае, url это массив байт. Т.е. когда ты делаешь, например, http-запрос, у тебя в нём только байты.
                  В питоне, конечно же, он представляется массивом символов, что вполне логично и понятно.

                  Но на самом деле, это всё бред. Думаю, что я не сильно погрешу против истины, если заявлю, что URL, это массив ascii-сиволов. Не ascii в хосте кодируется пуникодом, а в пути — utf-8, представленным в шестнадцатиричном виде с помощью того же ascii. По крайней мере так оно работает в http.
                • 0
                  Нет, не так, URL — это по сути своей строка, либо Request. Зачем пихать в неё массив байт?
                  Если тебе прямо так необходимо сделать из строки массив байт, то используй encode(xxx) с нужной тебе кодировкой.
                  "http://{:s}:{:d}/".format(host, port).encode('cp1251')
                  Правда если ты хочешь именно URL, и жаждешь самостоятельно перекодировать и отправлять байты, то тут тебя ждут отличные перспективы велосепеда. Используй строку.
                  • 0
                    это не мне необходимо, а www.python.org/dev/peps/pep-3333/#unicode-issues :)
                    хочу сказать, что если в python2 «суть строк» была вполне утино-однотипна, то разработчики p3k, соорудили для нас самостоятельную заморочку. что, например, в стеке wsgi приводит к нескончаемой череде этих самых decode() / encode().
                    • 0
                      Как раз нет, на уровне протокола уже нужно оперировать массивами байт. Благо методы у них примерно такие же. Зато не происходит подмена понятий байт-символ и нет этой вечной путаницы с кодировками на уровне строк, которые должны представляться однозначно на своём уровне абстракции.
                      • 0
                        уровни абстракции это компромисс между нужностью и удобством. и чем их меньше приходится создавать, тем лучше. как пишут в букваре, сообщение http представляет собой последовательность октетов. поэтому «массив байт» это тоже — абстракция. так вот, нужна-ли она, такая? ведь очевидно же, что в p3k удобства, в этом плане, стало меньше, а хорошо задокументированных заморочек — больше.
                        >>> '1' == '1'[0]
                        True
                        >>> b'1' == b'1'[0]
                        False
                        
                        • 0
                          Как раз это здорово, что понятия символа отделили от понятия байта. То что символ можно сравнивать с односимвольной строкой — это логично, и подобное лексическое сравнение есть везде. А вот второе бред — сравнивать массив байт с байтом. Естественно False. Всё логично, как и всегда в питоне.
                          Что до уровней абстракции, то они всегда возникают естественным путём. В данном случае строка имеет неизмеримо высший уровень абстракции нежели массив байт. Вы же не задумываетесь о кодировке, когда что-то пишете на бумаге.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Интересные публикации