Pull to refresh

Comments 16

У f-строк, есть одна особенность(недостаток): в них нельзя указывать спец символы со слэшами, например, '\n' '\t'.

У меня всегда работали. Проверил сейчас на python 3.8. Следующий код выводит и перевод строки, и табуляцию.
print(f'Line #1\n\tLine #2')
Действительно, так работает, но вот в фигурных скобках, указывать нельзя:

>>> a = ["a", "b"]
>>> f"{'\n'.join(a)}"
SyntaxError: f-string expression part cannot include a backslash

Спасибо за уточнение. Буду знать


Не сталкивался с этим ограничением, т.к. обычно более-менее сложные конструкции выношу в отдельные переменные :)

Я сам с удивлением узнал, впрочем, это действительно затрудняет парсинг и создаёт возможные уязвимости.
Вот здесь есть несколько примеров.
Как мы видим, cpython смог создать простую, но в то же время, эффективную реализацию строкового типа.
Это сарказм такой? Не знаю насчёт скорости, но наворотов тут даже больше, чем в C++ std::string, который тоже считается сложным (ради эффективности, понятно).
Ждём от Вас статьи про std::string. Я не в курсе, слышал только про 2 реализации всех строковых алгоритмов в java: для ascii и для не ascii строк и флаг, указывающий на тип.

Ну и самое простое — это хранить массив из 4 байт на каждый символ. Вот только эффективно ли это?

В cpython удалось добиться не большого расхода памяти, при этом скрыть особенности реализации от пользователя, благодаря чему он может, например, обращаться по индексу, в отличие от Rust или Julia.

А так ли важно, сколько там наворотов, если абстрация не течёт? :)
А так ли важно, сколько там наворотов, если абстрация не течёт? :)
Ну абстракция всё равно течёт, конечно. Они почти все текут. Да собственно ваш check_interned это показывает (там, кстати, можно писать не id(string) == id(string2), по-простому: string is strings… что, кстати, показывает как протечка в этой абстракции может вас «укусить» в реальной программе).

Но самая большая проблема со строками в Python3 — это то, что все эти навороты решают непонятно какую проблему, но понятно какую проблему они не решают: работу с реальными данными.

Потому что вся работа со строками в Python3 построена вокруг двух предположений:
1. Строки (текст) и бинарные данные строго отделены друг от друга.
2. Во всех случаях когда мы работаем со строками мы всегда заранее знаем кодировку.
Поскольку в реальном мире оба этих предположения неверны, то строки в Python — это боль и содомия. Чего одна история с path-like объектами стоит!

А в C++ строки не так интересны — хотя там они считаются сложными они на порядок меньше вот всего того, что тут навернули. Первый бит указывает на то короткая строка или длинная, оставшееся интерпретируется в зависимости от этого — собственно и всё.
Потому что вся работа со строками в Python3 построена вокруг двух предположений:
1. Строки (текст) и бинарные данные строго отделены друг от друга.
2. Во всех случаях когда мы работаем со строками мы всегда заранее знаем кодировку.
Поскольку в реальном мире оба этих предположения неверны, то строки в Python — это боль и содомия. Чего одна история с path-like объектами стоит!


Что для вас отстоят отдельно? Разные типы? Ну это логично, строки — частный случай бинарных данных, вполне можно навесить дополнительных проверок и операций, чтобы облегчить работу программисту. Как я уже упоминал, в некоторых языках даже индексирование не возможно.

Насчёт кодировок — константы всегда приходят из исходника с известной кодировкой, при чтении файлов, её тоже можно указать. Если он не известна то, что вы предлагаете делать?
Ну это логично, строки — частный случай бинарных данных, вполне можно навесить дополнительных проверок и операций, чтобы облегчить работу программисту.
Это у нормальных людей строки — это частный случай бинарных данных. В python3 — нет. Отсюда, собственно, появление path-like объектов: поскольку имя файлов (на на Linux, ни на Windows) не обязано быть валидной UTF-8 или UTF-16 строкой, то можно было легко создать файл, который в ранних версиях python-3 нельзя в принципе никак открыть.

То же самое с HTTP-серверами и с массой других протоколов. Ситуация, когда вперемешлу «в потоке байт» идут строки и «сырые» данные — исключением совершенно не является. Ситуация, когда вы вначале должны распарсить строку, а потом — узнаёте в какой она кодировки… тоже типична.

Всё это решается в Python3 втыканием всё большего и большего числа костылей в разные места (начиная с Python 3.5 поддерживается %, может со временем и format добавят).

Как я уже упоминал, в некоторых языках даже индексирование не возможно.
А в python возможно? Да неужели?
>>> 'Ху**ня'[4]
'н'
>>> 'Ху*ня'[4]
'я'
Это, по вашему, нормальная индексация?

Я вообще не знаю ни одного практически полезного алгоритма, для которого такая индексация годилась бы, а побайтовая индексация UTF-8 текста — не годилась бы.

Но за эту блажь платится усложнением реализации, замедлением и усложнением языка. Впрочем это всё в духе Python, так что, в принципе, неудивительно.

Python3 по сравнению с Python2 — это как C++ по сравнению с C: выстрелить в ногу слегка сложнее, но всё равно не очень сложно, а разрушений — намного больше.

Насчёт кодировок — константы всегда приходят из исходника с известной кодировкой, при чтении файлов, её тоже можно указать. Если он не известна то, что вы предлагаете делать?
Работать с данными как с последовательностью байт. А кодировку использовать там и тогда, когда это реально нужно.

Какой процент строк из вашей программы у вас отображается на экране? Зачем засорять работу с теми из них (а это, в типичном случае 90%, а во многих программах и 99%) сущностью, которая вам не нужна?
Я не понимаю ваших претензий.
Это у нормальных людей строки — это частный случай бинарных данных. В python3 — нет.

Давайте не переходить на уровень «настоящих шотландцев». Что вы подразумеваете, под тем, что в python3 строки не бинарные данные?

А в python возможно? Да неужели?
>>> 'Ху**ня'[4]
'н'
>>> 'Ху*ня'[4]
'я'

У вас в первом случае должна быть [3]? Ну выглядит как логичное и ожидаемое поведение, строка — последовательность символов, индексируясь по ней — получаем символы. Ещё раз есть языки, в которых на такой операции вы получите ошибку компиляции, это лучше?

Я вообще не знаю ни одного практически полезного алгоритма, для которого такая индексация годилась бы, а побайтовая индексация UTF-8 текста — не годилась бы.

1. encode вам в помощь.
2. ну и не работайте в таком случае не с python, зачем грызть кактус?) Есть C, который гораздо лучше подходит под обработку бинарных данных, python вообще не про это.

Но за эту блажь платится усложнением реализации, замедлением и усложнением языка. Впрочем это всё в духе Python, так что, в принципе, неудивительно.


Критикуешь — предлагай. Как вы посоветуете переписать реализацию? Можно хоть в виде PEP на оф. сайте. Всё сообщество будет вам благодарно.

Python3 по сравнению с Python2 — это как C++ по сравнению с C: выстрелить в ногу слегка сложнее, но всё равно не очень сложно, а разрушений — намного больше.


Всё смешалось, кони-люди. В C++ выстрелить в ногу сложнее или в C? Какие разрушения вы получите в случае с python? Я пытался, я менял служебные поля в рантайме, в большинстве случаев получал падение интерпретатора, никуда буферы не текли, стеки не переполнялись, произвольный код не выполнялся. Хотя достичь этого можно, но надо прям специально постараться, это не выстрел в ногу — это осознанный суицид.

Работать с данными как с последовательностью байт. А кодировку использовать там и тогда, когда это реально нужно.


Так используйте) Кто вам не разрешает?

Какой процент строк из вашей программы у вас отображается на экране? Зачем засорять работу с теми из них (а это, в типичном случае 90%, а во многих программах и 99%) сущностью, которая вам не нужна?


Вот здесь вообще не понял.
Что вы подразумеваете, под тем, что в python3 строки не бинарные данные?
То, что в них нельзя засунуть произвольную последовательность байт, очевидно. Например вы не можете имя файлы (которое в Windows не обязано быть валидной UTF-16 последовательностью, а в Linux валидной UTF-8 последовательностью) поместить в строку и проверить — не начинается оно с нужного вам префикса.

Ещё раз есть языки, в которых на такой операции вы получите ошибку компиляции, это лучше?
Лучше конечно. Отсуствие идиотского поведения всегда лучше его наличия. Хорошее поведение — ещё лучше, конечно, вот только при работе со строками его нет. В принципе нет — и всё.

Ну выглядит как логичное и ожидаемое поведение, строка — последовательность символов, индексируясь по ней — получаем символы.
Ну и кому и зачем это может быть нужно? Зачем оптимизировать работу под операции которые никогда не используются (или используются исключительно редко) за счёт операций, которые используются часто?

Есть C, который гораздо лучше подходит под обработку бинарных данных, python вообще не про это.
А про что он вообще тогда?

Как вы посоветуете переписать реализацию?
Никак — ошибка же в дизайне. Остаётся только смириться. Ну вот такой вот бессмысленный высер, который обошёлся индустрии в миллиарды и не дал никакой прибыли. Конструктивно — нужно убрать человека, который его затеял, чтобы больше такого не повторялось.

Это, впрочем, уже сделано, так что ничего менять уже не нужно.

В C++ выстрелить в ногу сложнее или в C?
В C++ сложнее наделать проблем гораздо сложнее, но если уж это удалось сделать — то разрушений намного больше.

То же самое с Python2 строками vs Python3 строками. Python2 позволяет легко сделать что-то не совсем правильно — но это быстро обваливается и легко чинится. В Python3 любое простое решение «правильно на 99.9%», но вот оставшийся 0.1% — исправить, зачастую, возможно только полными переписываением всей программы. Что дорого и сложно.

Так используйте) Кто вам не разрешает?
Python3. Масса операций, имеющих полный смысл для последовательностей байт — не работает с бинарными строками. А строковые операции не принимают произвольные последовательности байт. В Python2 — проблем не было.

Простейший пример, которым я всегда тыкаю в нос — простенький файл для работы со словариком и там буквально пяток функций для каждого языка. Которые просто возвращают список гласных, список согласных и так далее. Для английского, немецкого, польського и русского (там ещё что-то было — но эти ключевые). Функции, работающие с немецким рассчитаны на Latin-1, на польский — на Latin-2, Руссики — windows-1251… ну и комментарии в UTF-8 для удобства.

Всё это, ещё раз повторяю, в одном файле. Инструменты старого образца (Emacs, Far или тот же Python2) — вообще не проблема. Говорим, что это последовательность байт и всё. Что-то сделать в Python3… практически невоможно.

Ну потому что этот текстовый файл в Python3 строку — не лезет. Ну никак.

1. encode вам в помощь.
С этого момента — поподробнее: как его применять для редактирования подобного файла?
То, что в них нельзя засунуть произвольную последовательность байт, очевидно. Например вы не можете имя файлы (которое в Windows не обязано быть валидной UTF-16 последовательностью, а в Linux валидной UTF-8 последовательностью) поместить в строку и проверить — не начинается оно с нужного вам префикса.


Это проблемы python или ОС, которые сохраняют имена файлов в чём попало? Строка в python имеет определённую кодировку, вполне логично запретить туда записывать мусор. Для работы с файлами создали отдельный механизм, потому что это отдельная сущность. Зачем ломать из-за этого общий механизм?

Ну и кому и зачем это может быть нужно? Зачем оптимизировать работу под операции которые никогда не используются (или используются исключительно редко) за счёт операций, которые используются часто?


Ну если вам ненужно, очевидно, что всем не нужно. Напишите в почтовую рассылку, просветите заблудшие души.

Есть C, который гораздо лучше подходит под обработку бинарных данных, python вообще не про это.


А про что он вообще тогда?


Про читаемость. Это если одним словом, если несколькими — это к дзену, там про бинарные данные ничего нет.

Никак — ошибка же в дизайне. Остаётся только смириться. Ну вот такой вот бессмысленный высер, который обошёлся индустрии в миллиарды и не дал никакой прибыли. Конструктивно — нужно убрать человека, который его затеял, чтобы больше такого не повторялось.


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

В Python3 любое простое решение «правильно на 99.9%», но вот оставшийся 0.1% — исправить, зачастую, возможно только полными переписываением всей программы. Что дорого и сложно.


Утверждение верное для любой технологии, даже молотка.

Простейший пример, которым я всегда тыкаю в нос — простенький файл для работы со словариком и там буквально пяток функций для каждого языка. Которые просто возвращают список гласных, список согласных и так далее. Для английского, немецкого, польського и русского (там ещё что-то было — но эти ключевые). Функции, работающие с немецким рассчитаны на Latin-1, на польский — на Latin-2, Руссики — windows-1251… ну и комментарии в UTF-8 для удобства.

Всё это, ещё раз повторяю, в одном файле. Инструменты старого образца (Emacs, Far или тот же Python2) — вообще не проблема. Говорим, что это последовательность байт и всё. Что-то сделать в Python3… практически невоможно.

Ну потому что этот текстовый файл в Python3 строку — не лезет. Ну никак.


Это простой пример? Зачем вы храните разные кодировки в одном файле? В чём скрытый смысл? Как вы его обрабатываете сейчас? Чем? Зачем?

С этого момента — поподробнее: как его применять для редактирования подобного файла?


Читаете файл, как бинарный блоками, на блоке вызываете decode/encode
Это проблемы python или ОС, которые сохраняют имена файлов в чём попало?
Python, конечно. OS вы сменить не можете (вернее можете, но поскольку проблемы есть во всех современных OS, то это ничего не даст), а язык — можете (пока что Go выглядит неплохим вариантом).

Строка в python имеет определённую кодировку, вполне логично запретить туда записывать мусор.
Только если ваша задача — не написание программ для работы с реальными данными, а то, что один мой хороший знакомый называет «интеллектуальная мастурбация».

Для работы с файлами создали отдельный механизм, потому что это отдельная сущность.
Создали отдельную сущность, заметим, только в 2016м. Через восемь лет после выхода Python 3.0

Зачем ломать из-за этого общий механизм?
Потому что «общий механизм» должен работать с «общими данными», очевидно. Где невалидный Unicode — норма, а не исключение. И если существующие программы с этими данными работают (а они работают), то никто ради вас «общие данные» переделывать не будет.

Про читаемость. Это если одним словом, если несколькими — это к дзену, там про бинарные данные ничего нет.
А про корректность там чего-нибудь есть?

Кстати, а как вы подсчитали урон? Можете поделиться?
Примерно — тупо по методике COCOMO посчитать сколько денег ушло на бессмысленное переписывание кучи кода с Python2 на Python3.

Утверждение верное для любой технологии, даже молотка.
В случае с молотком — решается простым инструктажем. В случае с Python — увы, нет.

Это простой пример?
А что — сложный? Один текстовый файл, там меньше 1000 строк.

Зачем вы храните разные кодировки в одном файле?
Потому что так удобнее.

В чём скрытый смысл?
Работа с однобайтовыми кодировками банально быстрее, чем с многобайтовыми. А для словаря с одним языков (99% случаев) многобайтовая кодировка не нужна.

Как вы его обрабатываете сейчас?
Да чем угодно: emacs/vim/far/awk/perl/python2. Собственно его можно обрабатывать чем угодно, нельзя только Python3.

Зачем?
Ну например утилита для форматирования у нас на Python была до появления clang-format.

Читаете файл, как бинарный блоками, на блоке вызываете decode/encode
Ну и как, я извиняюсь, это может работать? Ни один из предложенных там вариантов не позволит мне сохранить данные после декодирования.

Понимаете — проблема Python3 в том, что он появился не в 1968м, а в 2008м. Когда наиболее распространённый тип данных в мире — это «поломанный Unicode». То есть это либо бинарные данные со вкраплениями UTF-8, либо UTF-8, где кое-где немножко что-то невалидно, либо «вроде-как-типа-что-то UTF-16, но на самом деле нет» (в Java и C#, теоретически, строки — это UTF-8, но на самом деле это почти нигде не энфорсится, так что любая последовательность 16-битовых значений принимается). В языках, которые предназначены для первого типа — легко обрабатываются и данные второго типа (WTF-8 в помощь). А вот с Python — вы никогда не можете быть уверены, что ваша проверенная и отлаженная программа вдруг не упадёт из-за того, что кто-то, вдруг, посмеет выбрать русский язык при установке вашей программы (была такая беда в Fedora — всё накрывалось медным тазом из-за того, что строка сообщавщая об успешном завершении установки выбрасывала UnicodeError… после чего инсталлятор успешно откатывал всю установку… решалось жёстким выключением компьютера в определённый момент).
Создали отдельную сущность, заметим, только в 2016м. Через восемь лет после выхода Python 3.0

Что подтверждает только то, что это не было первостепенной задачей ни для кого. Иначе ввели бы раньше.
Работа с однобайтовыми кодировками банально быстрее, чем с многобайтовыми. А для словаря с одним языков (99% случаев) многобайтовая кодировка не нужна.


Ещё раз, python не про скорость и не работу с бинарными данными, он про читаемость, файл с несколькими кодировками — это не к читаемости. Создатели python не предусматривали такой сценарий, естественно, он в таком формате неоптимален, но люди, смешивающие кодировки, должны страдать.
В тоже время куча людей создаёт сайты на django, тесты и скрипты автоматизации на python и не жалуется.

Зачем вы храните разные кодировки в одном файле?


Потому что так удобнее.


Вы уверенны? Новички, приходящие в вашу команду, как это воспринимают? Интуитивно понятно?

была такая беда в Fedora — всё накрывалось медным тазом из-за того, что строка сообщавщая об успешном завершении установки выбрасывала UnicodeError…


Текст — это сложно. Я про это отписал, как починили по итогу?

Ну и как, я извиняюсь, это может работать? Ни один из предложенных там вариантов не позволит мне сохранить данные после декодирования.

Почему? Вы прочли бинарные данные — перегнали их в текст, обработали — перегнали обратно в бинарные. Можно даже сразу обрабатывать бинарные, `b'my_string'` вам в помощь. Если формат файла более-менее постоянен и позволяет описать их константами.

И вы так и не написали, как реализованы строки в C++.
Что подтверждает только то, что это не было первостепенной задачей ни для кого.
Нет — подтверждает то, что разработчиков языка проблемы его пользователей мало волновали. Иначе бы о них подумали раньше и перешли бы на третью версию «несколько» быстрее. А в результате — ещё и сегодня есть учебные курсы, которые используют Python2 для обучения… то никакой смерти Python2 в 2020м не будет.

Во многом именно потому что работа со строками в Python3 менее удобна чем в Python2.

В тоже время куча людей создаёт сайты на django, тесты и скрипты автоматизации на python и не жалуется.
Ровно до того момента, пока их тесты и скрипты не начинают падать. Потом — да, жалобы возникают, но поскольку авторы этих скриптов уже завязли…

Вы уверенны? Новички, приходящие в вашу команду, как это воспринимают? Интуитивно понятно?
Последним новчиком, которому это нужно было воспринимать был, собственно я (добавлял русский язык, очевидно). Не заметил проблем. И Emacs и Vim всё отлично обрабатывают. Если бы пользовал Eclipse или что-то помоднее — может и были бы проблемы, я не знаю.

Текст — это сложно. Я про это отписал, как починили по итогу?
Через два года вышла новая версия инсталлятора, там этой проблемы вроде не было. Хотя я, на всякий случай, зарёкся использовать что-то, кроме en_US во время установки… вряд ли это можно считать «красивым» решением проблемы… но мы ж про читабельность, нам же работоспособность не нужна.

Почему? Вы прочли бинарные данные — перегнали их в текст, обработали — перегнали обратно в бинарные.
Потому что если там будут-таки бинарные данные то после того, как вы «перегоните их в текст» — они будут безнадёжно испорчены. Либо будет брошено исключение и вы не получите ничего, либо разные байты будут заменены на один, либо ещё что-нибудь в этом же роде произойдёт.

Да, можно попробовать что-то такое изобразить через codecs.register_error — но всё это выглядит крайне неудобно.

Если формат файла более-менее постоянен и позволяет описать их константами.
Ну формат файла в данном случае вы придставляете…

И вы так и не написали, как реализованы строки в C++.
Почему не написал? Написал. Общая идея там в одно предложение укладывается, а подробных статей уже и так имеется «в количестве» — зачем ещё одна?
Sign up to leave a comment.

Articles