Bất kỳ lỗi nào sử dụng unicode_literals trong Python 2.6?


101

Chúng tôi đã có cơ sở mã của mình chạy dưới Python 2.6. Để chuẩn bị cho Python 3.0, chúng tôi đã bắt đầu thêm:

từ __future__ nhập unicode_literals

vào các .pytệp của chúng tôi (khi chúng tôi sửa đổi chúng). Tôi tự hỏi liệu có ai khác đã làm điều này và gặp phải bất kỳ lỗi không rõ ràng nào không (có lẽ sau khi dành nhiều thời gian gỡ lỗi).

Câu trả lời:


101

Nguồn chính của các vấn đề mà tôi đã gặp phải khi làm việc với các chuỗi unicode là khi bạn trộn các chuỗi được mã hóa utf-8 với các chuỗi unicode.

Ví dụ, hãy xem xét các tập lệnh sau.

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

Đầu ra của việc chạy python one.pylà:

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)

Trong ví dụ này, two.namelà một chuỗi được mã hóa utf-8 (không phải unicode) vì nó không nhập unicode_literalsone.namelà một chuỗi unicode. Khi bạn kết hợp cả hai, python sẽ cố gắng giải mã chuỗi được mã hóa (giả sử đó là ascii) và chuyển đổi nó thành unicode và không thành công. Nó sẽ hoạt động nếu bạn đã làm print name + two.name.decode('utf-8').

Điều tương tự cũng có thể xảy ra nếu bạn mã hóa một chuỗi và cố gắng trộn chúng sau đó. Ví dụ, điều này hoạt động:

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

Đầu ra:

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

Nhưng sau khi thêm import unicode_literalsnó KHÔNG:

# 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

Đầu ra:

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)

Nó không thành công vì 'DEBUG: %s'là một chuỗi unicode và do đó python cố gắng giải mã html. Một số cách để sửa bản in đang thực hiện print str('DEBUG: %s') % htmlhoặc print 'DEBUG: %s' % html.decode('utf-8').

Tôi hy vọng điều này sẽ giúp bạn hiểu được các lỗi tiềm ẩn khi sử dụng chuỗi unicode.


11
Tôi khuyên bạn nên sử dụng các decode()giải pháp thay vì str()hoặc encode()các giải pháp: bạn càng sử dụng các đối tượng Unicode thường xuyên hơn, mã càng rõ ràng, vì những gì bạn muốn là thao tác các chuỗi ký tự, không phải mảng byte với mã hóa ngụ ý bên ngoài.
Eric O Lebigot

8
Vui lòng sửa thuật ngữ của bạn. when you mix utf-8 encoded strings with unicode onesUTF-8 và Unicode không phải là 2 bảng mã khác nhau; Unicode là một tiêu chuẩn và UTF-8 là một trong những bảng mã mà nó định nghĩa.
Kos

11
@Kos: Tôi nghĩ anh ấy có nghĩa là trộn "utf-8 chuỗi mã hóa" đối tượng với unicode (do đó giải mã) đối tượng . Cái trước là loại str, cái sau là loại unicode. Là đối tượng khác nhau, vấn đề có thể xảy ra nếu bạn cố gắng tổng hợp / concatenate / Nội suy họ
MestreLion

Điều này có áp dụng cho python>=2.6hoặc python==2.6không?
joar

16

Ngoài ra trong 2.6 (trước python 2.6.5 RC1 +) các ký tự unicode không hoạt động tốt với các đối số từ khóa ( issue4978 ):

Ví dụ, mã sau hoạt động mà không có unicode_literals, nhưng không thành công với TypeError: keywords must be stringnếu unicode_literals được sử dụng.

  >>> 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
Chỉ cần FYI, python 2.6.5 RC1 + đã sửa lỗi này.
Mahmoud Abdelkader

13

Tôi nhận thấy rằng nếu bạn thêm unicode_literalschỉ thị, bạn cũng nên thêm một số thứ như:

 # -*- coding: utf-8

đến dòng đầu tiên hoặc dòng thứ hai tệp .py của bạn. Ngược lại các dòng như:

 foo = "barré"

dẫn đến một lỗi như:

SyntaxError: Ký tự không phải ASCII '\ xc3' trong tệp mumble.py trên dòng 198,
 nhưng không có mã hóa nào được khai báo; xem http://www.python.org/peps/pep-0263.html
 để biết chi tiết

5
@IanMackinnon: Python 3 giả định rằng tập tin là UTF8 theo mặc định
endolith

3
@endolith: Nhưng Python 2 thì không, và nó sẽ gây ra lỗi cú pháp nếu bạn sử dụng các ký tự không phải ascii ngay cả trong nhận xét ! Vì vậy, IMHO # -*- coding: utf-8là một tuyên bố hầu như bắt buộc không phân biệt nếu bạn sử dụng unicode_literalshay không
MestreLion

Các -*-không yêu cầu; nếu bạn đang đi theo cách tương thích với emacs, tôi nghĩ bạn sẽ cần -*- encoding: utf-8 -*-(xem thêm -*-ở cuối). Tất cả những gì bạn cần là coding: utf-8(hoặc thậm chí =thay vì : ).
Chris Morgan,

2
Bạn có gặp lỗi này hay không from __future__ import unicode_literals.
Flimm

3
Khả năng tương thích của Emacs yêu cầu # -*- coding: utf-8 -*- với "mã hóa" (không phải "mã hóa" hoặc "mã hóa tệp" hoặc bất kỳ thứ gì khác - Python chỉ tìm kiếm "mã hóa" bất kể tiền tố nào).
Alex Dupuy

7

Cũng cần tính đến điều đó unicode_literalsẽ ảnh hưởng eval()nhưng không ảnh hưởng repr()(một hành vi không đối xứng mà imho là một lỗi), tức là eval(repr(b'\xa4'))sẽ không bằng b'\xa4'(như với Python 3).

Lý tưởng nhất, mã sau sẽ là bất biến, luôn hoạt động, cho tất cả các kết hợp của unicode_literalsvà sử dụng 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+

Xác nhận thứ hai xảy ra hoạt động, vì repr('\xa4')đánh giá u'\xa4'trong Python 2.7.


2
Tôi cảm thấy như vấn đề lớn hơn ở đây là bạn đang sử dụng reprđể tái tạo một đối tượng. Các reprtài liệu nêu rõ rằng đây là không một yêu cầu. Theo ý kiến ​​của tôi, điều này liên quan reprđến một cái gì đó hữu ích chỉ để gỡ lỗi.
jpmc 26

5

Có nhiều.

Có các thư viện và nội trang mong đợi các chuỗi không dung nạp unicode.

Hai ví dụ:

được xây dựng trong:

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

(hơi lạ) không hoạt động với unicode_literals: type () mong đợi một chuỗi.

thư viện:

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

không hoạt động: thư viện wx pubsub mong đợi một loại thông báo chuỗi.

Đầu tiên là bí truyền và dễ dàng sửa chữa với

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

nhưng cái thứ hai sẽ tàn phá nếu mã của bạn chứa đầy các lệnh gọi đến pub.sendMessage () (của tôi là).

Đặng nó hả?!?


3
Và những thứ loại cũng bị rò rỉ vào metaclasses - vì vậy trong Django bất kỳ chuỗi bạn khai báo trong class Meta:nênb'field_name'
Hamish Downer

2
Vâng ... trong trường hợp của tôi, tôi nhận ra rằng việc tìm kiếm và thay thế tất cả các chuỗi sendMessage bằng các phiên bản b 'là điều xứng đáng. Nếu bạn muốn tránh ngoại lệ "giải mã" đáng sợ, không có gì bằng cách sử dụng nghiêm ngặt unicode trong chương trình của bạn, chuyển đổi trên đầu vào và đầu ra khi cần thiết ("bánh sandwich unicode" được đề cập trong một số bài báo tôi đọc về chủ đề). Nhìn chung, unicode_literals đã là một chiến thắng lớn đối với tôi ...
GreenAsJade 28/09/13

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.