Чому НЕ слід використовувати sys.setdefaultencoding ("utf-8") у py-скрипті?


166

Я бачив кілька сценаріїв py, які використовують це у верхній частині сценарію. У яких випадках його слід використовувати?

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

2
є проблема з використанням цього в ipython,% час перестає працювати github.com/ipython/ipython/isissue/8071
seanv507

3
@ seanv507, прочитайте відповіді - її використання серйозно не рекомендується
Alastair McCormack


2
Чому це не точний дублікат Небезпеки sys.setdefaultencoding ('utf-8') ? Хоча цей запит (2010 р.) Передує цьому (2015 р.)? Але і на це запитання є хороші відповіді. Що робити? Крім того, щоб бути зрозумілим, це питання має сенс лише для Python 2, а не 3, але це ніде не позначено і не згадується.
smci

варто прочитати, перш ніж зануритися у відповіді SO: pythonhosted.org/kitchen/unicode-frustrations.html
ccpizza

Відповіді:


141

Відповідно до документації: Це дозволяє переходити від ASCII за замовчуванням до інших кодувань, таких як UTF-8, який виконуватиме Python, коли він повинен декодувати буфер рядків для однокодування.

Ця функція доступна лише під час запуску програми Python, коли Python сканує середовище. Її потрібно викликати в загальносистемному модулі, sitecustomize.pyПісля того, як цей модуль буде оцінено, setdefaultencoding()функція видаляється з sysмодуля.

Єдиний спосіб насправді використовувати його - це хак перезавантаження, який повертає атрибут.

Крім того, використання sys.setdefaultencoding()завжди було відмовлено , і це стало необов’язком у py3k. Кодування py3k провідне до "utf-8", а зміна його спричиняє помилку.

Я пропоную кілька вказівників для читання:


6
Чудові речі, хоча тут трохи смерті від занадто багато інформації. Я дізнався найбільш просто, зосередившись на цій статті: blog.notdot.net/2010/07/Getting-unicode-right-in-Python
mbb

3
Я хотів би додати, що кодування за замовчуванням також використовується для кодування (при записі, sys.stdoutколи у нього є Noneкодування, як, наприклад, при перенаправлення виводу програми Python).
Ерік О Лебігот

14
+1 за "використання sys.setdefaultencoding()завжди було
відсторонено

7
"провідний кабель до utf-8" не відповідає дійсності, він не є провідним, і це не завжди UTF-8. LC_ALL=en_US.UTF-8 python3 -c 'import sys; print(sys.stdout.encoding)'дає, UTF-8але LC_ALL=C python3 -c 'import sys; print(sys.stdout.encoding)'дає ANSI_X3.4-1968(або, можливо, щось інше)
Тіно,

7
@Tino, консольне кодування окремо від кодування за замовчуванням.
Аластер Маккормак

59

тл; д-р

Відповідь ніколи ! (якщо ви дійсно не знаєте, що робите)

У 9/10 разів рішення може бути вирішене за допомогою належного розуміння кодування / декодування.

1/10 людей мають неправильно визначений локальний елемент або оточення, і їх потрібно встановити:

PYTHONIOENCODING="UTF-8"  

у їхньому середовищі, щоб вирішити проблеми друку консолі.

Що це робить?

sys.setdefaultencoding("utf-8")(натиснуто, щоб уникнути повторного використання) змінює кодування / декодування за замовчуванням, що використовується, коли Python 2.x потребує перетворення Unicode () у str () (і навпаки), і кодування не дається. Тобто:

str(u"\u20AC")
unicode("€")
"{}".format(u"\u20AC") 

У Python 2.x кодування за замовчуванням встановлено на ASCII, і наведені вище приклади не вдасться:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)

(Моя консоль налаштована як UTF-8, отже "€" = '\xe2\x82\xac', звідси виняток \xe2)

або

UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)

sys.setdefaultencoding("utf-8")дозволить мені працювати на мене , але не обов'язково працювати для людей, які не використовують UTF-8. За замовчуванням ASCII гарантує, що припущення про кодування не вводяться в код

Консоль

sys.setdefaultencoding("utf-8")також має побічний ефект, що з'являється для виправлення sys.stdout.encoding, який використовується під час друку символів на консолі. Для встановлення цього Python використовує місце користувача (Linux / OS X / Un * x) або кодову сторінку (Windows). Іноді локальна локальність користувача порушується і просто потрібно PYTHONIOENCODINGвиправити кодування консолі .

Приклад:

$ export LANG=en_GB.gibberish
$ python
>>> import sys
>>> sys.stdout.encoding
'ANSI_X3.4-1968'
>>> print u"\u20AC"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)
>>> exit()

$ PYTHONIOENCODING=UTF-8 python
>>> import sys
>>> sys.stdout.encoding
'UTF-8'
>>> print u"\u20AC"

Що так погано з sys.setdefaultencoding ("utf-8") ?

Люди розробляли програму Python 2.x протягом 16 років, розуміючи, що кодування за замовчуванням - ASCII. UnicodeErrorМетоди обробки винятків були написані для обробки рядків для перетворень Unicode на рядках, які, як відомо, містять не-ASCII.

З https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/

def welcome_message(byte_string):
    try:
        return u"%s runs your business" % byte_string
    except UnicodeError:
        return u"%s runs your business" % unicode(byte_string,
            encoding=detect_encoding(byte_string))

print(welcome_message(u"Angstrom (Å®)".encode("latin-1"))

Перед встановленням дефолтного кодування цей код не міг би розшифрувати "Å" в кодуванні ascii, а потім введе обробник винятків, щоб відгадати кодування та правильно перетворити його в unicode. Друк: Angstrom (Å®) веде ваш бізнес. Після встановлення кодування за замовчуванням до utf-8 код виявить, що байт-стринг можна інтерпретувати як utf-8, і тому він буде маніпулювати даними та повертати це замість цього: Angstrom (Ů) веде ваш бізнес.

Зміна того, що має бути постійним, матиме драматичні наслідки для модулів, від яких ви залежите. Краще просто виправити дані, що надходять та виходять із коду.

Приклад проблеми

Хоча налаштування дефолтного кодування на UTF-8 не є першопричиною в наступному прикладі, він показує, як маскуються проблеми і як при зміні кодування вхідного коду нерозривно порушується: UnicodeDecodeError: 'utf8' кодек може не декодуйте байт 0x80 у позиції 3131: недійсний початковий байт


2
Хоча в цьому є сюрпризи sys.setdefaultencoding("utf-8"), добре змусити код поводитись більше, як Python 3. Зараз це 2017 рік. Навіть коли ви писали відповідь ще у 2015 році, я вважаю, що краще було дивитися вперед, а не назад. Насправді це було найпростішим рішенням для мене, коли я виявив, що мій код поводиться по-різному в Python 2, залежно від того, чи буде переспрямований вихід (дуже неприємна проблема для Python 2). # coding: utf-8Потрібно сказати, що я вже маю , і мені не потрібно жодних обхідних шляхів для Python 3 (я фактично маскую setdefaultencodingперевірку версії за допомогою версії).
Yongwei Wu

Це чудово, і він працює для вас, але sys.setdefaultencoding("utf-8")не робить ваш код Py 2.x сумісним з Python 3. Також він не фіксує зовнішні модулі, які передбачають кодування за замовчуванням - ASCII. Зробити ваш код Python 3 сумісним дуже просто і не вимагає цього неприємного злому. Наприклад, чому це спричиняє дуже реальні проблеми, дивіться мій досвід, коли Амазонка возиться з цим припущенням: stackoverflow.com/questions/39465220/…
Alastair McCormack

1
@AlastairMcCormack Ви рок, Мій сайт працює з місяців і не міг зрозуміти, що робити. Нарешті, PYTHONIOENCODING="UTF-8"допоміг моєму середовищу Python2.7 Django-1.11. Дякую.
Сем

Я знаю, що ви скопіювали приклад, але я можу знайти, який пакет має detect_encoding.
dlamblin

@dlamblin Приклад коду - довести цитату і не повинен використовуватись у вашому коді. Уявіть, що detect_encodingце метод, який може виявити кодування рядка на основі мовних підказок.
Аластер Маккормак

18
#!/usr/bin/env python
#-*- coding: utf-8 -*-
u = u'moçambique'
print u.encode("utf-8")
print u

chmod +x test.py
./test.py
moçambique
moçambique

./test.py > output.txt
Traceback (most recent call last):
  File "./test.py", line 5, in <module>
    print u
UnicodeEncodeError: 'ascii' codec can't encode character 
u'\xe7' in position 2: ordinal not in range(128)

на оболонці працює, відправляючи в sdtout не, так що це одне вирішення, писати в stdout.

Я зробив інший підхід, який не виконується, якщо sys.stdout.encoding не визначається, або іншими словами, потрібно спочатку експортувати PYTHONIOENCODING = UTF-8, щоб записати в stdout.

import sys
if (sys.stdout.encoding is None):            
    print >> sys.stderr, "please set python env PYTHONIOENCODING=UTF-8, example: export PYTHONIOENCODING=UTF-8, when write to stdout." 
    exit(1)


так, використовуючи той же приклад:

export PYTHONIOENCODING=UTF-8
./test.py > output.txt

буду працювати


3
Це не дає відповіді на запитання. Швидше деякі дотичні думки з цього приводу.
ivan_pozdeev

3
  • Перша небезпека полягає в reload(sys).

    Перезавантажуючи модуль, ви фактично отримуєте дві копії модуля під час виконання. Старий модуль є об'єктом Python, як і все інше, і залишається живим, поки на нього є посилання. Отже, половина об’єктів буде вказувати на старий модуль, а половина на новий. Коли ви внесете певні зміни, ви ніколи не побачите, що вона настає, коли якийсь випадковий об'єкт не побачить змін:

    (This is IPython shell)
    
    In [1]: import sys
    
    In [2]: sys.stdout
    Out[2]: <colorama.ansitowin32.StreamWrapper at 0x3a2aac8>
    
    In [3]: reload(sys)
    <module 'sys' (built-in)>
    
    In [4]: sys.stdout
    Out[4]: <open file '<stdout>', mode 'w' at 0x00000000022E20C0>
    
    In [11]: import IPython.terminal
    
    In [14]: IPython.terminal.interactiveshell.sys.stdout
    Out[14]: <colorama.ansitowin32.StreamWrapper at 0x3a9aac8>
  • Тепер, sys.setdefaultencoding()власне

    Все, на що це впливає, - це неявна конверсіяstr<->unicode . Тепер, чи utf-8є найбезпечніше кодування на планеті (сумісне із зворотним ASCII та всіма), перетворення зараз "просто працює", що може піти не так?

    Ну, що завгодно. А це небезпека.

    • Можливо, існує якийсь код, який покладається на те, UnicodeErrorщо викидається для введення без ASCII, або здійснює перекодування за допомогою обробника помилок, що тепер призводить до несподіваного результату. А оскільки весь код протестовано за замовчуванням, ви суворо перебуваєте на "непідтримуваній" території тут , і ніхто не дає вам гарантій того, як буде вести їх код.
    • Перекодування може призвести до несподіваних або непридатних результатів, якщо не все в системі використовує UTF-8, оскільки Python 2 насправді має кілька незалежних "кодових рядкових кодувань" . (Пам'ятайте, що програма повинна працювати для замовника, на обладнанні замовника.)
      • Знову ж таки, найгірше, що ви ніколи не дізнаєтесь про це, оскільки конверсія неявна - ви насправді не знаєте, коли і де це відбувається. (Python Zen, koan 2 ahoy!) Ви ніколи не дізнаєтесь, чому (і якщо) ваш код працює в одній системі і не працює на іншій. (Або ще краще, він працює в IDE і працює на консолі.)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.