gotchas ใด ๆ ที่ใช้ unicode_literals ใน Python 2.6?


101

เราได้รับฐานรหัสของเราที่ทำงานภายใต้ Python 2.6 แล้ว เพื่อเตรียมพร้อมสำหรับ Python 3.0 เราได้เริ่มเพิ่ม:

จาก __future__ นำเข้า unicode_literals

ลงใน.pyไฟล์ของเรา(เมื่อเราแก้ไข) ฉันสงสัยว่ามีใครทำสิ่งนี้อยู่หรือไม่และพบว่ามี gotcha ที่ไม่ชัดเจน (อาจใช้เวลาในการแก้ไขจุดบกพร่อง)

คำตอบ:


101

สาเหตุหลักของปัญหาที่ฉันเคยใช้กับสตริง Unicode คือเมื่อคุณผสมสตริงที่เข้ารหัส utf-8 กับสตริงที่มียูนิโคด

ตัวอย่างเช่นพิจารณาสคริปต์ต่อไปนี้

two.py

# encoding: utf-8
name = 'helló wörld from two'

one.py

# encoding: utf-8
from __future__ import unicode_literals
import two
name = 'helló wörld from one'
print name + two.name

ผลลัพธ์ของการทำงานpython one.pyคือ:

Traceback (most recent call last):
  File "one.py", line 5, in <module>
    print name + two.name
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)

ในตัวอย่างtwo.nameนี้เป็นสตริงที่เข้ารหัส utf-8 (ไม่ใช่ Unicode) เนื่องจากไม่ได้นำเข้าunicode_literalsและone.nameเป็นสตริง Unicode เมื่อคุณผสมทั้งสองอย่าง python จะพยายามถอดรหัสสตริงที่เข้ารหัส (สมมติว่าเป็น ascii) และแปลงเป็น Unicode และล้มเหลว print name + two.name.decode('utf-8')มันจะทำงานถ้าคุณไม่ได้ทำ

สิ่งเดียวกันนี้สามารถเกิดขึ้นได้หากคุณเข้ารหัสสตริงและพยายามผสมในภายหลัง ตัวอย่างเช่นการทำงานนี้:

# encoding: utf-8
html = '<html><body>helló wörld</body></html>'
if isinstance(html, unicode):
    html = html.encode('utf-8')
print 'DEBUG: %s' % html

เอาท์พุต:

DEBUG: <html><body>helló wörld</body></html>

แต่หลังจากเพิ่มimport unicode_literalsมันจะไม่:

# encoding: utf-8
from __future__ import unicode_literals
html = '<html><body>helló wörld</body></html>'
if isinstance(html, unicode):
    html = html.encode('utf-8')
print 'DEBUG: %s' % html

เอาท์พุต:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    print 'DEBUG: %s' % html
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 16: ordinal not in range(128)

มันล้มเหลวเนื่องจาก'DEBUG: %s'เป็นสายอักขระ Unicode htmlและดังนั้นจึงพยายามที่จะถอดรหัสหลาม สองวิธีในการแก้ไขการพิมพ์คือทำprint str('DEBUG: %s') % htmlหรือprint 'DEBUG: %s' % html.decode('utf-8')หรือ

ฉันหวังว่านี่จะช่วยให้คุณเข้าใจ gotchas ที่อาจเกิดขึ้นเมื่อใช้สตริง Unicode


11
ฉันขอแนะนำให้ใช้decode()โซลูชันแทนstr()หรือencode()โซลูชัน: ยิ่งคุณใช้อ็อบเจ็กต์ Unicode บ่อยเท่าไหร่โค้ดก็ยิ่งชัดเจนขึ้นเท่านั้นเนื่องจากสิ่งที่คุณต้องการคือการจัดการสตริงของอักขระไม่ใช่อาร์เรย์ของไบต์ที่มีการเข้ารหัสโดยนัยจากภายนอก
Eric O Lebigot

8
โปรดแก้ไขคำศัพท์ของคุณ when you mix utf-8 encoded strings with unicode onesUTF-8 และ Unicode ไม่มีการเข้ารหัส 2 แบบ Unicode เป็นมาตรฐานและ UTF-8 เป็นหนึ่งในการเข้ารหัสที่กำหนด
คอส

11
@Kos: ผมคิดว่าเขาหมายถึงการผสม "UTF-8 สตริงเข้ารหัส" วัตถุกับ Unicode (ถอดรหัสด้วยเหตุนี้) วัตถุ อดีตเป็นประเภทหลังคือประเภทstr unicodeเนื่องจากเป็นวัตถุที่แตกต่างกันปัญหาอาจเกิดขึ้นหากคุณพยายามรวม / ต่อ / แก้ไข
MestreLion

สิ่งนี้ใช้กับpython>=2.6หรือpython==2.6?
joar

16

นอกจากนี้ใน 2.6 (ก่อน python 2.6.5 RC1 +) Unicode literals เล่นไม่ดีกับอาร์กิวเมนต์คำหลัก ( issue4978 ):

ตัวอย่างโค้ดต่อไปนี้ใช้งานได้โดยไม่มี unicode_literals แต่ล้มเหลวด้วย TypeError: keywords must be stringถ้าใช้ unicode_literals

  >>> def foo(a=None): pass
  ...
  >>> foo(**{'a':1})
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
      TypeError: foo() keywords must be strings

17
เพียงแค่ FYI python 2.6.5 RC1 + ได้แก้ไขสิ่งนี้แล้ว
Mahmoud Abdelkader

13

ฉันพบว่าหากคุณเพิ่มunicode_literalsคำสั่งคุณควรเพิ่มสิ่งต่างๆเช่น:

 # -*- coding: utf-8

ไปยังบรรทัดแรกหรือบรรทัดที่สองของไฟล์. py ของคุณ บรรทัดอื่น ๆ เช่น:

 foo = "barré"

ส่งผลให้เกิดข้อผิดพลาดเช่น:

SyntaxError: อักขระที่ไม่ใช่ ASCII '\ xc3' ในไฟล์ mumble.py ในบรรทัดที่ 198
 แต่ไม่มีการประกาศการเข้ารหัส ดู http://www.python.org/peps/pep-0263.html
 เพื่อดูรายละเอียด

5
@IanMackinnon: Python 3 ถือว่าไฟล์เป็น UTF8 โดยค่าเริ่มต้น
endolith

3
@endolith: แต่ Python 2 ทำไม่ได้และจะทำให้เกิดข้อผิดพลาดทางไวยากรณ์หากคุณใช้ตัวอักษรที่ไม่ใช่ ascii แม้แต่ในความคิดเห็น ! ดังนั้น IMHO # -*- coding: utf-8จึงเป็นคำสั่งบังคับแทบไม่ว่าคุณจะใช้unicode_literalsหรือไม่
ก็ตาม

-*-ไม่จำเป็นต้อง; หากคุณกำลังใช้วิธีที่เข้ากันได้กับ emacs ฉันคิดว่าคุณต้องการ-*- encoding: utf-8 -*-(ดู-*-ในตอนท้ายด้วย) สิ่งที่คุณต้องการคือcoding: utf-8(หรือแม้แต่=แทนที่จะเป็น: )
Chris Morgan

2
คุณได้รับข้อผิดพลาดนี้ไม่ว่าจะเป็นคุณหรือไม่from __future__ import unicode_literalsก็ตาม
Flimm

3
ความเข้ากันได้ของ Emacs ต้องใช้ # -*- coding: utf-8 -*-กับ "การเข้ารหัส" (ไม่ใช่ "การเข้ารหัส" หรือ "การเข้ารหัสไฟล์" หรือสิ่งอื่นใด Python เพียงแค่มองหา "การเข้ารหัส" โดยไม่คำนึงถึงคำนำหน้าใด ๆ )
Alex Dupuy

7

นอกจากนี้ยังคำนึงถึงสิ่งที่unicode_literalจะส่งผลกระทบeval()แต่ไม่repr()(พฤติกรรมที่ไม่สมมาตรซึ่ง imho เป็นจุดบกพร่อง) กล่าวคือeval(repr(b'\xa4'))จะไม่เท่ากับb'\xa4'(เช่นเดียวกับ Python 3)

ตามหลักการแล้วรหัสต่อไปนี้จะเป็นค่าคงที่ซึ่งควรใช้งานได้เสมอสำหรับชุดค่าผสมทั้งหมดunicode_literalsและการใช้งาน Python {2.7, 3.x}:

from __future__ import unicode_literals

bstr = b'\xa4'
assert eval(repr(bstr)) == bstr # fails in Python 2.7, holds in 3.1+

ustr = '\xa4'
assert eval(repr(ustr)) == ustr # holds in Python 2.7 and 3.1+

การยืนยันครั้งที่สองเกิดขึ้นได้ผลเนื่องจากrepr('\xa4')ประเมินเป็นu'\xa4'Python 2.7


2
ฉันรู้สึกว่าปัญหาใหญ่กว่าตรงนี้คือคุณกำลังใช้reprสร้างวัตถุขึ้นมาใหม่ reprเอกสารอย่างชัดเจนระบุว่านี่คือไม่ได้ต้องการ ในความคิดของฉันสิ่งนี้ลดลงreprไปสู่สิ่งที่มีประโยชน์สำหรับการดีบักเท่านั้น
jpmc26

5

มีมากขึ้น.

มีไลบรารีและบิวด์อินที่คาดหวังสตริงที่ไม่ทนต่อยูนิโคด

สองตัวอย่าง:

ในตัว:

myenum = type('Enum', (), enum)

(esotic เล็กน้อย) ใช้ไม่ได้กับ unicode_literals: type () ต้องการสตริง

ห้องสมุด:

from wx.lib.pubsub import pub
pub.sendMessage("LOG MESSAGE", msg="no go for unicode literals")

ใช้ไม่ได้: ไลบรารี wx pubsub ต้องการชนิดข้อความสตริง

อดีตเป็นเรื่องลึกลับและแก้ไขได้ง่ายด้วย

myenum = type(b'Enum', (), enum)

แต่อย่างหลังจะทำลายล้างหากรหัสของคุณเต็มไปด้วยการโทรไปยัง pub.sendMessage () (ซึ่งเป็นของฉัน)

แดงใช่มั้ย!?


3
และสิ่งประเภทนี้ยังรั่วไหลไปสู่ ​​metaclasses ด้วยดังนั้นใน Django ทุกสตริงที่คุณประกาศclass Meta:ควรเป็นb'field_name'
Hamish Downer

2
ใช่ ... ในกรณีของฉันฉันรู้ว่ามันคุ้มค่าที่จะพยายามค้นหาและแทนที่สตริง sendMessage ทั้งหมดด้วยเวอร์ชัน b ' หากคุณต้องการหลีกเลี่ยงข้อยกเว้น "ถอดรหัส" ที่น่ากลัวไม่มีอะไรเหมือนกับการใช้ Unicode ในโปรแกรมของคุณอย่างเคร่งครัดโดยจะแปลงอินพุตและเอาต์พุตตามความจำเป็น ("แซนวิชยูนิโคด" ที่อ้างถึงในเอกสารบางฉบับที่ฉันอ่านในหัวข้อ) โดยรวมแล้ว unicode_literals เป็นชัยชนะที่ยิ่งใหญ่สำหรับฉัน ...
GreenAsJade

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.