Dlaczego nie jest to dokładny duplikat Dangers of sys.setdefaultencoding („utf-8”) ? Chociaż to (2010) pytanie poprzedza to (2015)? Ale to pytanie ma też dobre odpowiedzi. Co robić? Poza tym, aby było jasne, to pytanie ma sens tylko w Pythonie 2, a nie 3, ale nigdzie nie zostało to oznaczone ani wspomniane.
Zgodnie z dokumentacją: Pozwala to na przełączenie się z domyślnego ASCII na inne kodowanie, takie jak UTF-8, którego środowisko wykonawcze Pythona będzie używać, gdy będzie musiało zdekodować bufor ciągów na Unicode.
Ta funkcja jest dostępna tylko podczas uruchamiania Pythona, kiedy Python skanuje środowisko. Należy go wywołać w module ogólnosystemowym. sitecustomize.pyPo ocenie tego modułu setdefaultencoding()funkcja jest usuwana z sysmodułu.
Jedynym sposobem faktycznego użycia tego atrybutu jest hack przeładowania, który przywraca atrybut.
Ponadto, używanie sys.setdefaultencoding()zawsze było odradzane i stało się opcją w py3k. Kodowanie py3k jest na stałe połączone z „utf-8”, a jego zmiana powoduje błąd.
Chciałbym dodać, że domyślne kodowanie jest również używane do kodowania (podczas pisania do, sys.stdoutgdy ma Nonekodowanie, na przykład podczas przekierowywania wyjścia programu w Pythonie).
Eric O Lebigot
14
+1 za „używanie sys.setdefaultencoding()zawsze było odradzane”
jfs,
7
„Hard-wired to utf-8” nie jest prawdą, nie jest podłączony na stałe i nie zawsze UTF-8. LC_ALL=en_US.UTF-8 python3 -c 'import sys; print(sys.stdout.encoding)'daje, UTF-8ale LC_ALL=C python3 -c 'import sys; print(sys.stdout.encoding)'daje ANSI_X3.4-1968(a może coś innego)
Tino
7
@Tino, kodowanie konsoli jest niezależne od kodowania domyślnego.
Alastair McCormack
59
tl; dr
Odpowiedź brzmi: NIGDY ! (chyba że naprawdę wiesz, co robisz)
9/10 razy rozwiązanie można rozwiązać przy odpowiednim zrozumieniu kodowania / dekodowania.
1/10 osób ma nieprawidłowo zdefiniowaną lokalizację lub środowisko i musi ustawić:
PYTHONIOENCODING="UTF-8"
w ich środowisku, aby naprawić problemy z drukowaniem konsoli.
Co to robi?
sys.setdefaultencoding("utf-8")(przekreślony, aby uniknąć ponownego użycia) zmienia domyślne kodowanie / dekodowanie używane zawsze, gdy Python 2.x musi przekonwertować Unicode () na str () (i odwrotnie), a kodowanie nie jest podane. To znaczy:
str(u"\u20AC")
unicode("€")"{}".format(u"\u20AC")
W Pythonie 2.x domyślne kodowanie jest ustawione na ASCII, a powyższe przykłady zakończą się niepowodzeniem z:
UnicodeDecodeError:'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)
(Moja konsola jest skonfigurowana jako UTF-8, więc "€" = '\xe2\x82\xac'wyjątek włączony \xe2)
lub
UnicodeEncodeError:'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)
sys.setdefaultencoding("utf-8")pozwoli im działać dla mnie , ale niekoniecznie będzie działać dla osób, które nie używają UTF-8. Domyślne ASCII zapewnia, że założenia kodowania nie są wprowadzane do kodu
Konsola
sys.setdefaultencoding("utf-8")ma również efekt uboczny polegający na poprawianiu sys.stdout.encoding, używanym podczas drukowania znaków na konsoli. Python używa ustawień regionalnych użytkownika (Linux / OS X / Un * x) lub strony kodowej (Windows), aby to ustawić. Czasami ustawienia regionalne użytkownika są zepsute i wymagają tylko PYTHONIOENCODINGnaprawy kodowania konsoli .
Przykład:
$ 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"
€
Co jest takiego złego w sys.setdefaultencoding („utf-8”) ?
Od 16 lat ludzie rozwijają się przeciwko Pythonowi 2.x, wiedząc, że domyślnym kodowaniem jest ASCII. UnicodeErrormetody obsługi wyjątków zostały napisane w celu obsługi konwersji ciągów na Unicode w ciągach, które zawierają inne niż ASCII.
def welcome_message(byte_string):try:return u"%s runs your business"% byte_string
exceptUnicodeError:return u"%s runs your business"% unicode(byte_string,
encoding=detect_encoding(byte_string))print(welcome_message(u"Angstrom (Å®)".encode("latin-1"))
Przed ustawieniem domyślnego kodowania ten kod nie byłby w stanie zdekodować „Å” w kodowaniu ascii, a następnie wprowadziłby procedurę obsługi wyjątku, aby odgadnąć kodowanie i poprawnie przekształcić go w Unicode. Drukowanie: Angstrom (Å®) prowadzi Twoją firmę. Po ustawieniu domyślnego kodowania na utf-8, kod stwierdzi, że bajt_string można zinterpretować jako utf-8, więc zmieni dane i zwróci to: Angstrom (Ů) prowadzi Twoją firmę.
Zmiana tego, co powinno być stałą, będzie miała dramatyczny wpływ na moduły, na których polegasz. Lepiej jest po prostu naprawić dane przychodzące i wychodzące z kodu.
Chociaż są w sys.setdefaultencoding("utf-8")nim niespodzianki , dobrze jest sprawić, by kod zachowywał się bardziej jak Python 3. Teraz jest rok 2017. Nawet jeśli napisałeś odpowiedź w 2015 roku, myślę, że już lepiej było patrzeć w przyszłość niż w przeszłość. To było dla mnie najprostsze rozwiązanie, gdy zauważyłem, że mój kod zachowuje się inaczej w Pythonie 2 w zależności od tego, czy dane wyjściowe są przekierowywane (bardzo nieprzyjemny problem dla Pythona 2). Nie trzeba dodawać, że już mam # coding: utf-8i nie potrzebuję żadnych obejść dla Pythona 3 (właściwie muszę zamaskować setdefaultencodingsprawdzanie wersji przy użyciu).
Yongwei Wu
To świetnie i działa dla Ciebie, ale sys.setdefaultencoding("utf-8")nie sprawia, że kod Py 2.x jest zgodny z Pythonem 3. Nie naprawia też zewnętrznych modułów, które zakładają, że domyślnym kodowaniem jest ASCII. Dostosowanie kodu do Pythona 3 jest bardzo proste i nie wymaga tego okropnego hackowania. Na przykład, dlaczego powoduje to bardzo realne problemy, zobacz moje doświadczenia z Amazon mieszające się z tym założeniem: stackoverflow.com/questions/39465220/ ...
Alastair McCormack
1
@AlastairMcCormack you rock, Moja witryna istnieje od miesięcy i nie mogłem się dowiedzieć, co robić. Wreszcie PYTHONIOENCODING="UTF-8"pomogło mojemu środowisku Python2.7 Django-1.11. Dzięki.
sam
Wiem, że skopiowałeś przykład, ale mogę znaleźć pakiet detect_encoding.
dlamblin
@dlamblin Przykładowy kod służy do udowodnienia cytatu i nie powinien być używany w kodzie. Wyobraź sobie, że detect_encodingjest to metoda, która mogłaby wykryć kodowanie łańcucha na podstawie wskazówek językowych.
Alastair McCormack
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)
w powłoce działa, wysyłanie do sdtout nie, więc jest to jedno obejście, aby pisać na standardowe wyjście.
Zrobiłem inne podejście, które nie jest uruchamiane, jeśli sys.stdout.encoding nie jest zdefiniowane, lub innymi słowy, musisz najpierw wyeksportować PYTHONIOENCODING = UTF-8, aby zapisać na standardowe wyjście.
import sys
if(sys.stdout.encoding isNone):print>> sys.stderr,"please set python env PYTHONIOENCODING=UTF-8, example: export PYTHONIOENCODING=UTF-8, when write to stdout."
exit(1)
To nie odpowiada na zadane pytanie. Raczej styczne przemyślenia na ten temat.
ivan_pozdeev
3
Pierwsze niebezpieczeństwo tkwi w reload(sys).
Kiedy przeładowujesz moduł, w rzeczywistości otrzymujesz dwie kopie modułu w swoim środowisku wykonawczym. Stary moduł jest obiektem Pythona, jak wszystko inne, i pozostaje żywy, dopóki istnieją do niego odniesienia. Tak więc połowa obiektów będzie wskazywała na stary moduł, a połowa na nowy. Kiedy wprowadzisz jakąś zmianę, nigdy nie zobaczysz, że nadchodzi, gdy jakiś losowy obiekt nie zauważy zmiany:
(ThisisIPython 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]:importIPython.terminal
In[14]:IPython.terminal.interactiveshell.sys.stdout
Out[14]:<colorama.ansitowin32.StreamWrapper at 0x3a9aac8>
Może istnieć kod, który opiera się na UnicodeErrorwyrzucaniu dla danych wejściowych innych niż ASCII lub transkodowanie z obsługą błędów, co teraz daje nieoczekiwany wynik. A ponieważ cały kod jest testowany z ustawieniem domyślnym, jesteś tutaj ściśle na „nieobsługiwanym” terytorium i nikt nie daje Ci gwarancji, jak będzie się zachowywał jego kod.
Ponownie, najgorsze jest to, że nigdy się tego nie dowiesz, ponieważ konwersja jest niejawna - tak naprawdę nie wiesz, kiedy i gdzie się dzieje. (Python Zen, koan 2 ahoy!) Nigdy nie dowiesz się, dlaczego (i czy) twój kod działa w jednym systemie, a psuje się w innym. (Lub jeszcze lepiej, działa w IDE i psuje się w konsoli).
Odpowiedzi:
Zgodnie z dokumentacją: Pozwala to na przełączenie się z domyślnego ASCII na inne kodowanie, takie jak UTF-8, którego środowisko wykonawcze Pythona będzie używać, gdy będzie musiało zdekodować bufor ciągów na Unicode.
Ta funkcja jest dostępna tylko podczas uruchamiania Pythona, kiedy Python skanuje środowisko. Należy go wywołać w module ogólnosystemowym.
sitecustomize.py
Po ocenie tego modułusetdefaultencoding()
funkcja jest usuwana zsys
modułu.Jedynym sposobem faktycznego użycia tego atrybutu jest hack przeładowania, który przywraca atrybut.
Ponadto, używanie
sys.setdefaultencoding()
zawsze było odradzane i stało się opcją w py3k. Kodowanie py3k jest na stałe połączone z „utf-8”, a jego zmiana powoduje błąd.Proponuję kilka wskazówek do czytania:
źródło
sys.stdout
gdy maNone
kodowanie, na przykład podczas przekierowywania wyjścia programu w Pythonie).sys.setdefaultencoding()
zawsze było odradzane”UTF-8
.LC_ALL=en_US.UTF-8 python3 -c 'import sys; print(sys.stdout.encoding)'
daje,UTF-8
aleLC_ALL=C python3 -c 'import sys; print(sys.stdout.encoding)'
dajeANSI_X3.4-1968
(a może coś innego)tl; dr
Odpowiedź brzmi: NIGDY ! (chyba że naprawdę wiesz, co robisz)
9/10 razy rozwiązanie można rozwiązać przy odpowiednim zrozumieniu kodowania / dekodowania.
1/10 osób ma nieprawidłowo zdefiniowaną lokalizację lub środowisko i musi ustawić:
w ich środowisku, aby naprawić problemy z drukowaniem konsoli.
Co to robi?
(przekreślony, aby uniknąć ponownego użycia) zmienia domyślne kodowanie / dekodowanie używane zawsze, gdy Python 2.x musi przekonwertować Unicode () na str () (i odwrotnie), a kodowanie nie jest podane. To znaczy:sys.setdefaultencoding("utf-8")
W Pythonie 2.x domyślne kodowanie jest ustawione na ASCII, a powyższe przykłady zakończą się niepowodzeniem z:
(Moja konsola jest skonfigurowana jako UTF-8, więc
"€" = '\xe2\x82\xac'
wyjątek włączony\xe2
)lub
pozwoli im działać dla mnie , ale niekoniecznie będzie działać dla osób, które nie używają UTF-8. Domyślne ASCII zapewnia, że założenia kodowania nie są wprowadzane do kodusys.setdefaultencoding("utf-8")
Konsola
ma również efekt uboczny polegający na poprawianiusys.setdefaultencoding("utf-8")
sys.stdout.encoding
, używanym podczas drukowania znaków na konsoli. Python używa ustawień regionalnych użytkownika (Linux / OS X / Un * x) lub strony kodowej (Windows), aby to ustawić. Czasami ustawienia regionalne użytkownika są zepsute i wymagają tylkoPYTHONIOENCODING
naprawy kodowania konsoli .Przykład:
Co jest takiego złego w
sys.setdefaultencoding („utf-8”)?Od 16 lat ludzie rozwijają się przeciwko Pythonowi 2.x, wiedząc, że domyślnym kodowaniem jest ASCII.
UnicodeError
metody obsługi wyjątków zostały napisane w celu obsługi konwersji ciągów na Unicode w ciągach, które zawierają inne niż ASCII.Od https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/
Zmiana tego, co powinno być stałą, będzie miała dramatyczny wpływ na moduły, na których polegasz. Lepiej jest po prostu naprawić dane przychodzące i wychodzące z kodu.
Przykładowy problem
Chociaż ustawienie domyślnego kodowania na UTF-8 nie jest główną przyczyną w poniższym przykładzie, pokazuje, jak problemy są maskowane i jak, gdy zmienia się kodowanie wejściowe, kod psuje się w nieoczywisty sposób: UnicodeDecodeError: kodek 'utf8' może 't dekoduj bajt 0x80 na pozycji 3131: nieprawidłowy bajt początkowy
źródło
sys.setdefaultencoding("utf-8")
nim niespodzianki , dobrze jest sprawić, by kod zachowywał się bardziej jak Python 3. Teraz jest rok 2017. Nawet jeśli napisałeś odpowiedź w 2015 roku, myślę, że już lepiej było patrzeć w przyszłość niż w przeszłość. To było dla mnie najprostsze rozwiązanie, gdy zauważyłem, że mój kod zachowuje się inaczej w Pythonie 2 w zależności od tego, czy dane wyjściowe są przekierowywane (bardzo nieprzyjemny problem dla Pythona 2). Nie trzeba dodawać, że już mam# coding: utf-8
i nie potrzebuję żadnych obejść dla Pythona 3 (właściwie muszę zamaskowaćsetdefaultencoding
sprawdzanie wersji przy użyciu).sys.setdefaultencoding("utf-8")
nie sprawia, że kod Py 2.x jest zgodny z Pythonem 3. Nie naprawia też zewnętrznych modułów, które zakładają, że domyślnym kodowaniem jest ASCII. Dostosowanie kodu do Pythona 3 jest bardzo proste i nie wymaga tego okropnego hackowania. Na przykład, dlaczego powoduje to bardzo realne problemy, zobacz moje doświadczenia z Amazon mieszające się z tym założeniem: stackoverflow.com/questions/39465220/ ...PYTHONIOENCODING="UTF-8"
pomogło mojemu środowisku Python2.7 Django-1.11. Dzięki.detect_encoding
.detect_encoding
jest to metoda, która mogłaby wykryć kodowanie łańcucha na podstawie wskazówek językowych.w powłoce działa, wysyłanie do sdtout nie, więc jest to jedno obejście, aby pisać na standardowe wyjście.
Zrobiłem inne podejście, które nie jest uruchamiane, jeśli sys.stdout.encoding nie jest zdefiniowane, lub innymi słowy, musisz najpierw wyeksportować PYTHONIOENCODING = UTF-8, aby zapisać na standardowe wyjście.
więc, używając tego samego przykładu:
będzie działać
źródło
Pierwsze niebezpieczeństwo tkwi w
reload(sys)
.Kiedy przeładowujesz moduł, w rzeczywistości otrzymujesz dwie kopie modułu w swoim środowisku wykonawczym. Stary moduł jest obiektem Pythona, jak wszystko inne, i pozostaje żywy, dopóki istnieją do niego odniesienia. Tak więc połowa obiektów będzie wskazywała na stary moduł, a połowa na nowy. Kiedy wprowadzisz jakąś zmianę, nigdy nie zobaczysz, że nadchodzi, gdy jakiś losowy obiekt nie zauważy zmiany:
Teraz
sys.setdefaultencoding()
dobrzeWszystko, na co ma wpływ, to niejawna konwersja
str<->unicode
. Czyutf-8
jest to najbardziej rozsądne kodowanie na tej planecie (kompatybilne wstecz z ASCII i wszystkimi innymi), konwersja „po prostu działa”, co może się nie udać?Cóż, wszystko. I to jest niebezpieczeństwo.
UnicodeError
wyrzucaniu dla danych wejściowych innych niż ASCII lub transkodowanie z obsługą błędów, co teraz daje nieoczekiwany wynik. A ponieważ cały kod jest testowany z ustawieniem domyślnym, jesteś tutaj ściśle na „nieobsługiwanym” terytorium i nikt nie daje Ci gwarancji, jak będzie się zachowywał jego kod.źródło