14:47 

Brötchen mit Käse

Заболекарь
Мегакрендель: заколебарь, жаболекарь, зомболекарь, лежебокарь
Второй пайтон меня злит. По-хорошему его давно уже пора бы сдать в музей, поставить за стекло и говорить людям «прикиньте, вот на этом тоже когда-то писали». Но жизнь не мармелад и программу приходится тестировать и под ним.

Вот, скажем, обычное третьепайтоновское 'Brötchen mit {}'.format('Käse'), которое возвращает 'Brötchen mit Käse'. Что ему соответствует во втором? А во втором было два типа строк и, соответственно, четыре комбинации, и все делают не совсем то:

u'Brötchen mit {}'.format('Käse') кинет UnicodeDecodeError.
'Brötchen mit {}'.format(u'Käse') кинет UnicodeEncodeError.
'Brötchen mit {}'.format('Käse') вернёт 'Br\xc3\xb6tchen mit K\xc3\xa4se'.
u'Brötchen mit {}'.format(u'Käse') вернёт u'Br\xf6tchen mit K\xe4se'.

(если последние две строки напечатать при помощи print, выглядят они одинаково, Brötchen mit Käse. Это чтобы люди легче путались. Вести себя они, однако, будут по-разному)

Оператор % ведёт себя в этом плане похоже на .format, но всё же чуть иначе: 'Brötchen mit %s' % u'Käse' кинет не UnicodeEncodeError, но UnicodeDecodeError.

Всё это я заметил благодаря Флориану, который держит рабочие файлы в каталоге по имени Arbeitsfläche.

@темы: root@глупыйпингвин:~#

URL
Комментарии
2016-06-08 в 15:00 

CD_Eater
тролль - это не только ценный жир, но и 3-4 легкоусвояемых коммента ежедневно
похоже, что третий вариант возвращает UTF-8, а четвёртый - в кодировке локали ОС (latin-1)
а исходник-то был в какой кодировке?

2016-06-08 в 16:49 

Заболекарь
Мегакрендель: заколебарь, жаболекарь, зомболекарь, лежебокарь
CD_Eater, во втором пайтоне есть два типа, на одном пики точены один зовётся str, его литералы выглядят "вот так" или 'вот так', другой unicode, его литералы выглядят u"вот так" или u'вот так'. Поведение первого из них зависит от фазы луны наибезобразнейшим образом (скажем, литерал 'ы' на одном компьютере будет равен '\xd1\x8b' и иметь длину 2, как если бы его закодировали в utf-8, а на другом компе будет равен '\xfb', как если бы его закодировали в cp1251). Поведение второго типа более человеческое, u'ы' равен u'\u044b'.

Отвечает ли это на твой вопрос про кодировку исходника?

URL
2016-06-08 в 17:47 

CD_Eater
тролль - это не только ценный жир, но и 3-4 легкоусвояемых коммента ежедневно
ну, обычное дело
один тип - юникодный UTF-16, чтобы хранить строки, не представимые в текущей локали
второй тип - обычный, он использует ту кодировку, которую ты ему дал сам, не интерпретируя строку и не пытаясь переводить ни в какую кодировку, это тупая последовательность байт (что называется, 8-bit clean)
если файл с исходным кодом программы содержит '\xd1\x8b', то и питоновская строка будет содержать те же самые байты

надеюсь, я правильно объяснил, почему
литерал 'ы' на одном компьютере будет равен '\xd1\x8b' и иметь длину 2, как если бы его закодировали в utf-8, а на другом компе будет равен '\xfb',
?

претензии про фазы луны следует адресовать к твоему редактору, а не к питону
просто посмотри свой исходник в хекс-редакторе

2016-06-08 в 19:40 

Заболекарь
Мегакрендель: заколебарь, жаболекарь, зомболекарь, лежебокарь
CD_Eater, мне кажется, ты не вполне понимаешь ситуацию. Я не знаю, к каким языкам ты привык, но в пайтоне вовсе не обязан где-то существовать исходник, который ты взял и напечатал в редакторе, затем сохранил на диск и который можно после этого прямо вот взять и посмотреть. Интерпретатор может хранить историю, но не обязательно в удобном для просмотра формате — скажем, жюпытер-кутиконсоль использует для этого sqlite-файл где-то в недрах каталога .ipython. Его можно посмотреть гекс-редактором и убедиться, что пайтоновые строки он хранит как utf-8. Тем не менее, это не исходник, а база данных, в которой хранится история. А исходника нет и никогда не было. Если же он всё-таки есть — тогда 'ä' и u'ä' в файле на диске могут выглядеть почти одинаково, 27 c3 a4 27 и 75 27 c3 a4 27, зато строки, которые из этого получит второй пайтон (если в файле указано # -*- coding: utf-8 -*-), будут содержать c3 a4 первая и e4 вторая. Откуда e4? А вот оттуда. Разница как между строкой '17' и интом 17, которые в исходнике тоже похоже выглядят, но тем не менее разные вещи.

И да, про два типа ты тоже недопонял. Когда есть юникодные строки, а есть последовательности байтов — это хороший подход. В третьем пайтоне — а он появился ещё в 2008-м — именно он и используется. В нём все str стали юникодные, тип unicode стал не нужен и исчез, зато появились как раз массивы байтов, в которых, собственно, можно хранить байты. При этом b'Käse' кинет SyntaxError, а не будет неявно решать, b'K\xe4se' это или всё-таки b'K\xc3\xa4se' или всё-таки ошибка кодирования, print(b'K\xe4se') напечатает b'K\xe4se', а не будет искать, во что бы это \xe4 имплицитно раскодировать, чтобы сделать вид, что это такая строка. Но во втором пайтоне вместо строк и байтов есть именно что два типа строк, и из этого проистекают многия беды и мерзости. Они очень похожи, но слегка-слегка различаются поведением, и это полный пиздец и множество сложновоспроизводимых ошибок.

URL
2016-06-08 в 20:20 

CD_Eater
тролль - это не только ценный жир, но и 3-4 легкоусвояемых коммента ежедневно
Заболекарь, но в пайтоне вовсе не обязан где-то существовать исходник
ясен пень
назовём это "символьный поток, который поступает на вход сканера при лексическом разборе"

ты правда не понимаешь, что когда питон создаёт строку по строковому литералу, ему нужно взять откуда-то информацию о кодовой странице?
и поскольку обычно в исходном коде (или символьном потоке) такой информации нет, то стандартом в очень многих языках программирования является такой тип строки: "тут хранится строка в той кодировке, которую использовал юзер, но компилятор не знает в какой именно" - это и есть "обычные" строки
юникодные строки - другое дело, там происходит обязательная конвертация в утф-16, и компилятору приходится брать (из первой строчки файла) информацию о кодировке, или предполагать какую-то кодировку по умолчанию, если явной информации нет
когда ты используешь "обычные строки", ответственность за правильность результата (зависящего от угадывания кодировки) лежит на юзере
а когда ты думаешь, что питон должен что-то делать сам (когда он не должен), то ты и получаешь полную хню на выходе и пытаешься обвинять питона (и фазы луны)

а вот сравнивать с базой данных некорректно - там может применяться дополнительное перекодирование строк в соответствии с форматом этой базы


из-за подобных сложностей с непониманием кто за что ответственнен по части кодировки - многие языки программирования переводятся на только-юникод
этим удаётся уменьшить количество вопросов от недо-программистов "почему у меня крякозябры?"
(джава как самый быдлокодеро-толерантный язык перешла на только-юникод одной из первых)

2016-06-08 в 20:56 

Заболекарь
Мегакрендель: заколебарь, жаболекарь, зомболекарь, лежебокарь
CD_Eater, ну и привет. Я говорю, что он должен угадывать? Я говорю, что он должен при малейшем намёке на необходимость угадывания кидать SyntaxError и убегать в закат, улюлюкая и размахивая труселями над головой. Я не знаю, что для тебя значит «ответственность лежит на юзере», но если ты подразумеваешь «ошибки должны всплывать как можно позже и только при очень тщательном тестировании на всех возможных наборах входных данных, а пытаться найти их автоматически не комильфо», то это так себе подход. Ошибки вида «а здесь у нас массив байтов, содержащий два байта, 屁 и 股» или «а здесь мы вызовем на массиве байтов метод, который имеет смысл только для строк» интерпретатор может (и должен; и в третьем пайтоне так и делает) выдавать сразу, а не надеяться, что как-то пронесёт.

URL
2016-06-08 в 22:00 

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

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

и я не верю, что ты раньше не работал с такими языками, т.к. их - большинство
почти все языки програмирования - locale agnostic

2016-06-08 в 22:26 

Заболекарь
Мегакрендель: заколебарь, жаболекарь, зомболекарь, лежебокарь
CD_Eater, мне кажется, ты всё ещё не очень понимаешь, о чём идёт разговор. Давай чуток конкретики. Как ты думаешь, как во втором пайтоне поведёт себя выражение u"abcd" + "efgh"? Напоминаю, первая строка — юникодная, вторая — нет, + в данном случае значит конкатенацию строк. А как в третьем пайтоне поведёт себя аналогичное выражение "abcd" + b"efgh", где перед плюсом стоит строка, а после него массив байтов? Какое поведение лично тебе привычно?

URL
   

Эх, разум, да ещё разум

главная