Опасности sys.setdefaultencoding('utf-8')
В Python 2 существует тенденция обескураживать установку sys.setdefaultencoding('utf-8')
. Может ли кто-нибудь перечислить реальные примеры проблем с этим? Аргументы типа it is harmful
или it hides bugs
звучат не очень убедительно.
ОБНОВЛЕНИЕ. Обратите внимание, что этот вопрос относится только к utf-8
, а не к изменению кодировки по умолчанию "в общем случае".
Приведите несколько примеров кода, если сможете.
Ответы
Ответ 1
Оригинальный плакат попросил код, который показывает, что коммутатор вреден, за исключением того, что он "скрывает" ошибки, не связанные с коммутатором.
Резюме выводов
Основываясь на опыте и доказательствах, которые я собрал, вот выводы, к которым я пришел.
-
Настройка стандартного кодирования для UTF-8 в настоящее время безопасна, за исключением специализированных приложений, обрабатывающих файлы из систем, не поддерживающих unicode.
-
"Официальное" отклонение коммутатора основано на причинах, которые больше не соответствуют для подавляющего большинства конечных пользователей (а не для поставщиков библиотек), поэтому мы должны перестать препятствовать пользователям устанавливать его.
-
Работа с моделью, которая по умолчанию корректно обрабатывает Unicode, намного лучше подходит для приложений для межсистемных коммуникаций, чем вручную работать с API-интерфейсами Unicode.
Эффективно, очень часто изменяя кодировку по умолчанию , избегает ряда головных болей пользователя в подавляющем большинстве случаев использования, Да, бывают ситуации, когда программы, посвященные множественным кодировкам, будут молчаливо ошибочно, но поскольку этот переключатель можно включить по частям, это не проблема в коде конечного пользователя.
Более важно то, что включение этого флага является реальным преимуществом кода пользователей, как путем сокращения накладных расходов, связанных с ручным управлением преобразованиями Unicode, загромождением кода и уменьшением его удобочитаемости, а также предотвращением возможных ошибок при сбое программиста чтобы сделать это правильно во всех случаях.
Поскольку эти утверждения в значительной степени противоположны официальной линии связи Python, я думаю, что объяснение этих выводов оправданно.
Примеры успешного использования модифицированного defaultencoding в дикой природе
-
Дэйв Мальком из Fedora полагал, что всегда правы. Он предложил после изучения рисков изменить распределение шириной def.enc. = UTF-8 для всех пользователей Fedora.
Жесткий факт, хотя Python сломался, - это только поведение хеширования я указанное в списке, которое никогда не подхватывает какой-либо другой противник в основном сообществе в качестве причины для беспокоиться или даже того же человека при работе с пользовательскими билетами.
Резюме Fedora: По общему признанию, само изменение было описано как "дико непопулярное" с основными разработчиками, и его обвинили в непоследовательности с предыдущими версиями.
-
Есть 3000 проектов в openhub. У них медленный интерфейс поиска, но, просматривая его, я считаю, что 98% используют UTF-8. Ничего не найдено о неприятных сюрпризах.
-
Есть 18000 (!) ветвей магистрали github с изменением.
В то время как изменение " непопулярно, в основном сообществе его довольно популярны в пользовательской базе. Хотя это можно игнорировать, поскольку пользователи, как известно, используют хакерские решения, я не думаю, что это важный аргумент из-за моего следующего пункта.
-
Из-за этого существует только 150 bugreports всего на GitHub. При эффективном 100% изменение кажется положительным, а не отрицательным.
Чтобы обобщить существующие проблемы, с которыми столкнулись люди, я просмотрел все вышеупомянутые билеты.
-
Общение def.enc. для UTF-8 обычно вводится, но не удаляется в процессе закрытия проблемы, чаще всего в качестве решения. Некоторые более крупные из них объясняют это как временное исправление, учитывая "плохую прессу", но гораздо реже журналисты ошибок просто рад о исправить.
-
Несколько (1-5?) проектов изменили свой код, выполнив преобразования типов вручную, чтобы им больше не нужно было изменять значение по умолчанию.
-
В двух случаях я вижу, что кто-то утверждает это с def.enc. установленный в UTF-8, приводит к полному отсутствию вывода полностью, не объясняя тестовую настройку. Я не смог проверить заявку, и я протестировал ее, и нашел обратное, чтобы быть правдой.
-
Один утверждаетего "система" может зависеть от ее изменения, но мы не узнаем, почему.
-
У одного (и только одного) была настоящая причина его избежать: ipython либо использует сторонний модуль, либо тест-бегун модифицировал свой процесс неконтролируемым образом (никогда не оспаривается тот факт, что изменение def.enc было поддержано его сторонниками только на время настройки интерпретатора, то есть когда "владеют" процессом).
-
Я нашел нулевое указание на то, что разные хэши "é" и u'é "вызывают проблемы в реальном коде.
-
Python не "ломает"
После изменения настройки на UTF-8 никакая функция Python, охватываемая модульными тестами, не работает иначе, чем без коммутатора. Сам коммутатор не тестируется вообще.
-
На bugs.python.org сообщается о разочарованных пользователях
Примеры здесь, здесь или здесь
(часто связанные с официальной строкой предупреждения)
Первый показывает, как установлен переключатель в Азии (сравните также с аргументом github).
-
Ian Bicking опубликовал свою поддержку, всегда позволяющую это поведение.
Я могу постоянно поддерживать свои системы и коммуникации UTF-8, все будет лучше. Я действительно не вижу недостатка. Но почему Python делает это SO DAMN HARD [...] Я чувствую, что кто-то решил, что они умнее меня, но я не уверен, что верю им.
-
Martijn Fassen, опровергая Ian, признал, что ASCII, возможно, был ошибочным в первую очередь.
Я считаю, что если, скажем, Python 2.5, поставляемый с кодировкой UTF-8 по умолчанию, это фактически ничего не сломает. Но если бы я сделал это для своего Python, у меня скоро возникли проблемы, когда я передал свой код кому-то еще.
-
В Python3 они не "практикуют то, что они проповедуют"
Вопреки любому def.enc. изменить так жестко из-за кода, зависящего от окружающей среды, или косности, обсуждение здесь вращается вокруг Python3 с его 'unicode sandwich' парадигмой и соответствующими требуемыми неявными предположениями.
Далее они создали возможности для написания допустимого кода Python3, например:
>>> from 褐褑褒褓褔褕褖褗褘 import *
>>> def 空手(合氣道): あいき(ど(合氣道))
>>> 空手(う힑힜('👏 ') + 흾)
💔
-
DiveIntoPython рекомендует его.
-
В этой теме сам Guido советует a профессиональный конечный пользователь, чтобы использовать зависящую от процесса среду с коммутатором, установленным для "создания настраиваемой среды Python для каждого проекта".
Основная причина, по которой разработчики стандартной библиотеки Python 2.x не хотят, чтобы вы могли установить кодировку по умолчанию в своем приложении, заключается в том, что стандартная библиотека написана с предположением о том, что кодировка по умолчанию установлена, и никакие гарантии относительно правильной работы стандартной библиотеки не могут быть сделаны при ее изменении. Для этой ситуации нет тестов. Никто не знает, что не удастся, когда. И вы (или, что еще хуже, ваши пользователи) вернетесь к нам с жалобами, если стандартная библиотека вдруг начнет делать то, чего вы не ожидали.
-
Jython предлагает изменить его на лету даже в модулях.
-
PyPy сделал не поддержка reload (sys) - но вернул его в пользовательский запрос в течение одного дня без вопросов. Сравните с " вы делаете это неправильно "отношение CPython, требование без доказательства это "корень зла".
Завершение этого списка Я подтверждаю, что можно построить модуль, который выдает сообщение , потому что измененной конфигурации интерпретатора делает следующее:
def is_clean_ascii(s):
""" [Stupid] type agnostic checker if only ASCII chars are contained in s"""
try:
unicode(str(s))
# we end here also for NON ascii if the def.enc. was changed
return True
except Exception, ex:
return False
if is_clean_ascii(mystr):
<code relying on mystr to be ASCII>
Я не думаю, что это допустимый аргумент, потому что человек, который написал этот модуль приема с двумя типами, явно знал об ASCII и не ASCII-строках и знал бы о кодировании и декодировании.
Я думаю, что это доказательство более чем достаточно, так как изменение этого параметра в большинстве случаев не приводит к каким-либо проблемам в реальных кодовых базах.
Ответ 2
Поскольку вы не всегда хотите, чтобы ваши строки были автоматически декодированы в Unicode, или, что самое важное, ваши объекты Unicode автоматически закодированы в байтах. Поскольку вы запрашиваете конкретный пример, вот один из них:
Возьмите веб-приложение WSGI; вы создаете ответ, добавляя продукт внешнего процесса в список, в цикле, и этот внешний процесс дает вам кодированные байты UTF-8:
results = []
content_length = 0
for somevar in some_iterable:
output = some_process_that_produces_utf8(somevar)
content_length += len(output)
results.append(output)
headers = {
'Content-Length': str(content_length),
'Content-Type': 'text/html; charset=utf8',
}
start_response(200, headers)
return results
Это здорово, прекрасно и работает. Но тогда ваш сотрудник приходит и добавляет новую функцию; вы также предоставляете метки, и они локализованы:
results = []
content_length = 0
for somevar in some_iterable:
label = translations.get_label(somevar)
output = some_process_that_produces_utf8(somevar)
content_length += len(label) + len(output) + 1
results.append(label + '\n')
results.append(output)
headers = {
'Content-Length': str(content_length),
'Content-Type': 'text/html; charset=utf8',
}
start_response(200, headers)
return results
Вы протестировали это на английском и все еще работает, отлично!
Однако библиотека translations.get_label()
фактически возвращает Unicode values , а при переключении языкового стандарта метки содержат символы, отличные от ASCII.
Библиотека WSGI записывает эти результаты в сокет, и все значения Юникода автоматически зашифровываются для вас, так как вы устанавливаете setdefaultencoding()
в UTF-8, но вы рассчитали всю длину. Это будет слишком коротким, поскольку UTF-8 кодирует все за пределами диапазона ASCII более чем с одним байтом.
Все это игнорирует возможность того, что вы фактически работаете с данными в другом кодеке; вы можете писать латинский-1 + Юникод, и теперь у вас есть неправильный заголовок длины и сочетание кодировок данных.
Если бы вы не использовали sys.setdefaultencoding()
, исключение было бы поднято, и вы знали, что у вас есть ошибка, но теперь ваши клиенты жалуются на неполные ответы; в конце страницы отсутствуют байты, и вы не совсем знаете, как это произошло.
Обратите внимание, что в этом сценарии нет даже сторонних библиотек, которые могут или не могут зависеть от значения по умолчанию ASCII. Параметр sys.setdefaultencoding()
является глобальным, применяя к всем код, запущенный в интерпретаторе. Насколько вы уверены, что в этих библиотеках нет проблем с неявным кодированием или декодированием?
То, что Python 2 кодирует и декодирует между типами str
и unicode
, неявно может быть полезным и безопасным, когда вы имеете дело только с данными ASCII. Но вам действительно нужно знать, когда вы смешиваете Unicode и байтовые данные строки случайно, вместо того, чтобы покрывать его глобальной кистью и надеяться на лучшее.
Ответ 3
В первую очередь: многие противники изменения по умолчанию enc утверждают, что его немой, потому что его даже изменение сравнения ascii
Я считаю, что справедливо ясно, что, подчиняясь первому вопросу, я вижу, что никто не защищает ничего, кроме отклонения от Ascii до UTF-8.
Пример setdefaultencoding ('utf-16'), как представляется, всегда просто выдвигается теми, кто выступает против его изменения; -)
С m = {'a': 1, 'é': 2} и файл 'out.py':
# coding: utf-8
print u'é'
Тогда:
+---------------+-----------------------+-----------------+
| DEF.ENC | OPERATION | RESULT (printed)|
+---------------+-----------------------+-----------------+
| ANY | u'abc' == 'abc' | True |
| (i.e.Ascii | str(u'abc') | 'abc' |
| or UTF-8) | '%s %s' % ('a', u'a') | u'a a' |
| | python out.py | é |
| | u'a' in m | True |
| | len(u'a'), len(a) | (1, 1) |
| | len(u'é'), len('é') | (1, 2) [*] |
| | u'é' in m | False (!) |
+---------------+-----------------------+-----------------+
| UTF-8 | u'abé' == 'abé' | True [*] |
| | str(u'é') | 'é' |
| | '%s %s' % ('é', u'é') | u'é é' |
| | python out.py | more | 'é' |
+---------------+-----------------------+-----------------+
| Ascii | u'abé' == 'abé' | False, Warning |
| | str(u'é') | Encoding Crash |
| | '%s %s' % ('é', u'é') | Decoding Crash |
| | python out.py | more | Encoding Crash |
+---------------+-----------------------+-----------------+
[*]: Результат принимает одно и то же. См. Ниже.
При просмотре этих операций изменение кодировки по умолчанию в вашей программе может выглядеть не так уж плохо, что дает вам результаты "ближе" к тому, чтобы иметь только данные Ascii.
Что касается поведения хеширования (in) и len(), вы получаете то же самое, что и в Ascii (подробнее о результатах ниже). Эти операции также показывают, что между строк юникода и байта существуют существенные различия, которые могут вызывать логические ошибки, если они игнорируются вами.
Как уже отмечалось: это процесс широкий вариант, поэтому у вас есть только один выстрел, чтобы выбрать его - вот почему разработчики библиотеки никогда не должны это делать никогда, а чтобы их внутренности были в порядке, поэтому что им не нужно полагаться на неявные преобразования python.
Они также должны четко документировать то, что они ожидают и возвращают, и отрицают ввод, что они не пишут lib (например, функцию нормализации, см. Ниже).
= > Написание программ с этим параметром делает риском для других использовать модули вашей программы в своем коде, по крайней мере, без фильтрации ввода.
Примечание. Некоторые оппоненты утверждают, что def.enc. это даже системный вариант (через sitecustomize.py), но в последнее время во время программной контейнеризации (докера) каждый процесс можно запустить в идеальной среде без накладных расходов.
Что касается поведения хэширования и len():
Он сообщает вам, что даже с измененным def.enc. вы все еще не можете не знать о типах строк, которые вы обрабатываете в своей программе. u '' и '' - это разные последовательности байтов в памяти - не всегда, а в целом.
Поэтому при тестировании убедитесь, что ваша программа ведет себя правильно и с данными без Ascii.
Некоторые говорят о том, что хэши могут стать неравными при изменении значений данных - хотя из-за неявных преобразований операции "==" остаются равными - это аргумент против изменения def.enc.
Я лично не разделяю это, поскольку поведение хэширования остается таким же, как и без его изменения. До сих пор не было убедительного примера нежелательного поведения из-за этого параметра в процессе, который я 'сам'.
В целом, в отношении setdefaultencoding ( "utf-8" ): ответ, если его немой или нет, должен быть более сбалансированным.
Это зависит.
В то время как это предотвращает сбои, например. при выполнении операций str() в операторе журнала - цена является более высокой вероятностью появления неожиданных результатов позже, поскольку неправильные типы делают ее более длинной в коде, правильное функционирование которого зависит от определенного типа.
Ни в коем случае это не должно быть альтернативой для изучения разницы между строками байтов и строками Unicode для вашего собственного кода.
Наконец, установка кодировки по умолчанию из Ascii не облегчает вам жизнь для обычных текстовых операций, таких как len(), нарезка и сравнение - вы должны предположить, что (байтовый) stringyfying все с UTF-8 для решения проблем здесь.
К сожалению, это вообще не так.
Результаты '==' и len() - это far более сложная проблема, чем можно было бы подумать, но даже с типом того же с обеих сторон.
W/o def.enc. изменено, "==" всегда не выполняется для не Ascii, как показано в таблице. С его помощью он работает - иногда:
Юникод действительно стандартизовал около миллиона символов мира и дал им число, но, к сожалению, нет однозначности в отношении 1:1 между глифами, отображаемыми пользователю в устройствах вывода, и символами, из которых они генерируются.
Чтобы мотивировать вас исследовать это: Имея два файла, j1, j2, написанные с той же программой с использованием той же кодировки, содержащей пользовательский ввод:
>>> u1, u2 = open('j1').read(), open('j2').read()
>>> print sys.version.split()[0], u1, u2, u1 == u2
Результат: 2.7.9 Хосе Хосе Ложно (!)
Используя функцию print в качестве функции в Py2, вы видите причину: К сожалению, существует два способа кодирования одного и того же символа: акцент "e":
>>> print (sys.version.split()[0], u1, u2, u1 == u2)
('2.7.9', 'Jos\xc3\xa9', 'Jose\xcc\x81', False)
Какой глупый кодек вы могли бы сказать, но это не ошибка кодека. Его проблема в unicode как таковая.
Так даже в Py3:
>>> u1, u2 = open('j1').read(), open('j2').read()
>>> print sys.version.split()[0], u1, u2, u1 == u2
Результат: 3.4.2 Хосе Хосе Ложно (!)
= > Независимо от Py2 и Py3, фактически независимо от любого используемого вами языка вычислений: Чтобы написать качественное программное обеспечение, вам, вероятно, придется "нормализовать" все пользовательские ввод. Стандарт Unicode стандартизовал нормализацию.
В Python 2 и 3 функция unicodedata.normalize является вашим другом.
Ответ 4
Пример реального слова # 1
Он не работает в модульных тестах.
Тест-бегун (nose
, py.test
,...) сначала инициализирует sys
, а затем обнаруживает и импортирует ваши модули. К этому времени слишком поздно менять кодировку по умолчанию.
К тому же достоинству это не работает, если кто-то запускает ваш код в качестве модуля, так как сначала начинается их инициализация.
И да, смешение str
и unicode
и использование неявного преобразования только продвигает проблему дальше по строке.
Ответ 5
Мы должны знать одно:
Python 2 использует sys.getdefaultencoding()
для декодирования/кодирования между str
и unicode
![conversion between str and unicode]()
поэтому, если мы изменим кодировку по умолчанию, будут возникать всевозможные несовместимые проблемы. например:
# coding: utf-8
import sys
print "你好" == u"你好"
# False
reload(sys)
sys.setdefaultencoding("utf-8")
print "你好" == u"你好"
# True
Дополнительные примеры:
Тем не менее, я помню, что есть какой-то блог, предлагающий использовать unicode, когда это возможно, и только битовую строку при работе с I/O. Я думаю, что если вы будете следовать этому соглашению, жизнь будет намного проще. Можно найти больше решений: