Neden bir py betiğinde sys.setdefaultencoding (“utf-8”) kullanmamalıyız?


166

Senaryo üst kısmında bunu kullanan birkaç py komut dosyaları gördüm. Hangi durumlarda kullanılmalı?

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

2
Bunu ipython ile kullanmada bir sorun var,% zaman çalışmayı durduruyor github.com/ipython/ipython/issues/8071
seanv507

3
@ seanv507, cevapları okuyun - kullanmak ciddi bir şekilde cesaretini kırdı
Alastair McCormack


2
Bu nasıl sys.setdefaultencoding ('utf-8') Tehlikelerinin tam bir kopyası değildir ? Her ne kadar bu (2010) bunu önlüyor (2015)? Ama bu sormanın da iyi cevapları var. Ne yapalım? Ayrıca, açıkçası, bu soru sadece 3 değil Python 2'de mantıklıdır, ancak bu hiçbir yerde etiketlenmedi veya belirtilmedi.
smci

SO cevaplarına dalmadan
ccpizza

Yanıtlar:


141

Belgelere göre: Bu, varsayılan ASCII'den, Python çalışma zamanının unicode'a bir dize arabelleğinin kodunu çözmek gerektiğinde kullanacağı UTF-8 gibi diğer kodlamalara geçmenizi sağlar.

Bu işlev yalnızca Python ortamı tararken Python başlatma zamanında kullanılabilir. Sistem çapında bir modülde çağrılmalıdır sitecustomize.py, Bu modül değerlendirildikten sonra setdefaultencoding()işlev sysmodülden kaldırılır .

Bunu gerçekten kullanmanın tek yolu, özelliği geri getiren bir yeniden yükleme hack'idir.

Ayrıca, kullanımı sys.setdefaultencoding()her zaman cesaretini kırmıştır ve py3k'te bir op-haline dönüşmüştür. Py3k kodlaması "utf-8" ile kablolanmıştır ve değiştirilmesi bir hataya neden olur.

Okumak için bazı işaretçiler öneririm:


6
Harika şeyler, ama burada çok fazla bilgi nedeniyle biraz ölüm var. En çok bu makaleye odaklandığımı öğrendim: blog.notdot.net/2010/07/Getting-unicode-right-in-Python
mbb

3
Ben varsayılan kodlama de için kullanıldığını eklemek istiyorum kodlayan (yazarken sys.stdoutbir olduğunda Nonebir Python programının çıkışını yönlendirme yaparken gibi kodlama).
Eric O Lebigot

14
"Kullanımı sys.setdefaultencoding()her zaman cesaret kırıldı"
jfs

7
'utf-8'e kablolu' doğru değil, kablolu değil ve her zaman değil UTF-8. LC_ALL=en_US.UTF-8 python3 -c 'import sys; print(sys.stdout.encoding)'verir UTF-8ama LC_ALL=C python3 -c 'import sys; print(sys.stdout.encoding)'verir ANSI_X3.4-1968(ya da belki başka bir şey)
Tino

7
@Tino, konsol kodlaması varsayılan kodlamadan ayrıdır.
Alastair McCormack

59

tl; Dr.

Cevap ASLA değil ! (ne yaptığınızı gerçekten bilmiyorsanız)

9/10 kez çözüm, uygun kodlama / kod çözme anlayışı ile çözülebilir.

1/10 kişinin yanlış tanımlanmış bir yerel ayarı veya ortamı vardır ve şunları ayarlaması gerekir:

PYTHONIOENCODING="UTF-8"  

ortamlarında konsol yazdırma sorunlarını gidermek için.

Bu ne işe yarıyor?

sys.setdefaultencoding("utf-8")(yeniden kullanmaktan kaçınmak için), Python 2.x'in bir Unicode'u () bir str () 'e (ve tersi) dönüştürmesi gerektiğinde kullanılan varsayılan kodlama / kod çözmeyi değiştirir ve kodlama verilmez. yani:

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

Python 2.x'te, varsayılan kodlama ASCII olarak ayarlanmıştır ve yukarıdaki örnekler aşağıdakilerle başarısız olacaktır:

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

(Konsolum UTF-8 olarak yapılandırıldı, bu "€" = '\xe2\x82\xac'nedenle istisna açık \xe2)

veya

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

sys.setdefaultencoding("utf-8")bunların benim için çalışmasına izin verecek , ancak UTF-8 kullanmayan kişiler için çalışmayabilir. Varsayılan ASCII, kodlama varsayımlarının koda dönüştürülmemesini sağlar

Konsol

sys.setdefaultencoding("utf-8")sys.stdout.encodingkarakterleri konsola yazdırırken kullanılan, düzeltmek gibi görünen bir yan etkisi de vardır . Python bunu ayarlamak için kullanıcının yerel ayarını (Linux / OS X / Un * x) veya kod sayfasını (Windows) kullanır. Bazen bir kullanıcının yerel ayarı bozulur ve yalnızca konsol kodlamasınıPYTHONIOENCODING düzeltmek gerekir .

Misal:

$ 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") ile ilgili bu kadar kötü olan nedir ?

İnsanlar 16 yıldır Python 2.x'e karşı varsayılan kodlamanın ASCII olduğunu anlıyorlar. UnicodeErrorASCII dışı içerdiği belirlenen dizelerdeki Unicode dönüşümlerini dizeye işlemek için kural dışı durum işleme yöntemleri yazılmıştır.

Gönderen 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"))

Varsayılan kodlamayı ayarlamadan önce bu kod, ascii kodlamasındaki “Å” kodunun kodunu çözemez ve kodlamayı tahmin etmek ve düzgün bir şekilde unicode'a dönüştürmek için istisna işleyicisine girerdi. Yazdırma: Angstrom (Å®) işinizi yürütür. Varsayılan kodlamayı utf-8 olarak ayarladıktan sonra kod, byte_string öğesinin utf-8 olarak yorumlanabildiğini görür ve böylece verileri değiştirir ve bunun yerine bunu döndürür: Angstrom (Ů) işinizi yürütür.

Sabit olması gereken şeyi değiştirmek, bağımlı olduğunuz modüller üzerinde dramatik etkiler yaratacaktır. Kodunuza girip çıkan verileri düzeltmek daha iyidir.

Örnek problem

Varsayılan kodlamanın UTF-8 olarak ayarlanması, aşağıdaki örnekte temel neden olmasa da, sorunların nasıl maskelentiğini ve giriş kodlaması değiştiğinde, kodun açık bir şekilde nasıl bozulduğunu gösterir: UnicodeDecodeError: 'utf8' codec bileşeni 3131 konumundaki bayt 0x80 kod çözme: geçersiz başlangıç ​​baytı


2
Sürprizler olsa sys.setdefaultencoding("utf-8")da, kodun Python 3 gibi daha iyi davranması iyidir. 2017 şimdi. Cevabı 2015 yılında geri yazsanız bile, geriye bakmak yerine dört gözle beklemek daha iyi olduğunu düşünüyorum. Aslında benim için en basit çözüm, kodumun yeniden yönlendirilip yönlendirilmemesine (Python 2 için çok kötü bir sorun) bağlı olarak Python 2'de farklı davrandığını bulduğumda. Söylemeye gerek yok, ben zaten var # coding: utf-8ve Python 3 için herhangi bir geçici çözüm gerekmez (aslında setdefaultencodingkullanarak sürüm denetimi maskelemek zorunda ).
Yongwei Wu

Bu harika ve sizin için çalışıyor ancak sys.setdefaultencoding("utf-8")Py 2.x kodunuzu Python 3 ile uyumlu hale getirmiyor. Varsayılan kodlamanın ASCII olduğunu varsayan harici modülleri de düzeltmiyor. Kodunuzu Python 3 uyumlu hale getirmek çok basittir ve bu kötü hack gerektirmez. Örneğin, bunun neden çok gerçek sorunlara neden olduğunu, Amazon ile bu varsayımla uğraştığım deneyime bakın: stackoverflow.com/questions/39465220/…
Alastair McCormack

1
@AlastairMcCormack you rock, Sitem aylardır beri ve ne yapacağımı anlayamadım. Son olarak, PYTHONIOENCODING="UTF-8"Python2.7 Django-1.11 ortamına yardımcı oldu. Teşekkürler.
sam

Örneği kopyaladığınızı biliyorum, ancak paketin ne olduğunu bulabilirim detect_encoding.
dlamblin

@dlamblin Kod örneği alıntıyı kanıtlamak içindir ve kodunuzda kullanılması beklenmemektedir. Bunun detect_encoding, ipuçlarına dayalı bir dizenin kodlamasını algılayabilen bir yöntem olduğunu düşünün .
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)

kabuk üzerinde çalışır, sdtout'a gönderme değil, bu yüzden stdout'a yazmak için bir çözüm var.

Sys.stdout.encoding tanımlanmamışsa veya başka bir deyişle, stdout'a yazmak için önce PYTHONIOENCODING = UTF-8 dışa aktarmaya ihtiyaç duyulduğunda çalıştırılmayan başka bir yaklaşım yaptım.

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)


yani, aynı örneği kullanarak:

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

çalışacak


3
Bu, sorulan soruya cevap vermiyor. Bu konuda bazı teğetsel düşünceler.
ivan_pozdeev

3
  • İlk tehlike yatıyor reload(sys).

    Bir modülü yeniden yüklediğinizde, aslında çalışma sürenizde modülün iki kopyasını alırsınız . Eski modül, diğer her şey gibi bir Python nesnesidir ve referansları olduğu sürece hayatta kalır. Böylece, nesnelerin yarısı eski modüle, yarısı da yeni modüle işaret edecek. Bazı değişiklikler yaptığınızda, bazı rastgele nesneler değişikliği görmediğinde hiçbir zaman geleceğini görmezsiniz:

    (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>
  • Şimdi, sys.setdefaultencoding()uygun

    Etkilediği tek şey örtük dönüşümdürstr<->unicode . Şimdi, utf-8gezegendeki en san kodlama (ASCII ve hepsi ile geriye dönük uyumlu), dönüşüm şimdi "sadece çalışıyor", ne yanlış gidebilir?

    Şey, her şey. Ve bu tehlike.

    • UnicodeErrorASCII olmayan giriş için atılana dayanan veya şimdi bir beklenmedik sonuç üreten bir hata işleyici ile kod dönüştürme yapan bazı kodlar olabilir . Ve tüm kodlar varsayılan ayar ile test edildiğinden, kesinlikle burada "desteklenmeyen" bölgede bulunuyorsunuz ve hiç kimse size kodlarının nasıl davranacağını garanti etmiyor.
    • Kodlama , sistemdeki her şey UTF-8 kullanmıyorsa beklenmedik veya kullanılamaz sonuçlar üretebilir, çünkü Python 2 aslında birden fazla bağımsız "varsayılan dize kodlamasına" sahiptir . (Bir programın müşteri için, müşterinin ekipmanında çalışması gerektiğini unutmayın.)
      • Yine, en kötü şey, dönüşümün dolaylı olduğu için asla bilemeyeceksiniz - bunun ne zaman ve nerede olduğunu gerçekten bilmiyorsunuz. (Python Zen, koan 2 ahoy!) Kodunuzun neden bir sistemde çalıştığını (ve başka bir sistemde çalıştığını) asla bilemezsiniz. (Ya da daha iyisi, IDE'de çalışır ve konsolda kırılır.)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.