在Python 2.6中使用unicode_literals有任何陷阱吗?


101

我们已经获得了在Python 2.6下运行的代码库。为了准备Python 3.0,我们开始添加:

从__future__导入unicode_literals

进入我们的.py文件(我们对其进行修改)。我想知道是否还有其他人正在这样做并且遇到了任何非显而易见的陷阱(也许在花费大量时间进行调试之后)。

Answers:


101

我处理unicode字符串的主要问题来源是将utf-8编码的字符串与unicode的字符串混合使用。

例如,考虑以下脚本。

py

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

一个

# 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字符串,因此python尝试解码html。修复打印件的几种方法正在执行print str('DEBUG: %s') % htmlprint 'DEBUG: %s' % html.decode('utf-8')

我希望这可以帮助您了解使用unicode字符串时的潜在陷阱。


11
我建议使用decode()解决方案,而不是str()or encode()解决方案:您使用Unicode对象的次数越多,代码就越清晰,因为您想要的是操纵字符串,而不是使用外部隐式编码的字节数组。
Eric O Lebigot

8
请修正您的术语。when you mix utf-8 encoded strings with unicode onesUTF-8和Unicode没有2种不同的编码。Unicode是一种标准,而UTF-8是它定义的编码之一。
2012年

11
@Kos:我认为他的意思是将“ utf-8编码的字符串” 对象与unicode(因此已解码)对象混合使用。前者是类型str,后者是类型unicode。作为不同的对象,如果您尝试对它们进行求和/连接/插值,可能会出现问题
MestreLion 2012年

这适用于python>=2.6还是python==2.6
2013年

16

同样在2.6中(在python 2.6.5 RC1 +之前),unicode文字不能与关键字参数配合使用(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
仅供参考,python 2.6.5 RC1 +已修复此问题。
Mahmoud Abdelkader 2010年

13

我确实发现,如果添加unicode_literals指令,则还应该添加如下内容:

 # -*- coding: utf-8

.py文件的第一行或第二行。否则,例如:

 foo = "barré"

导致错误,例如:

语法错误:第198行的文件mumble.py中的非ASCII字符'\ xc3',
 但未声明编码;参见http://www.python.org/peps/pep-0263.html
 详情

5
@IanMackinnon:Python 3假定默认情况下文件为UTF8
endolith

3
@endolith:但是Python 2不会,如果您在注释中使用非ascii字符,它将给出语法错误!因此,恕我直言,# -*- coding: utf-8是一个几乎强制性的声明,无论如果你使用unicode_literals与否
MestreLion

-*-不是必需的; 如果您要使用与emacs兼容的方式,我认为您会需要-*- encoding: utf-8 -*-(另请参见-*-末尾)。您需要的只是coding: utf-8(或什至=不是: )。
克里斯·摩根

2
无论是否,您都会收到此错误from __future__ import unicode_literals
Flimm

3
Emacs兼容性要求 # -*- coding: utf-8 -*-使用“编码”(而不是“ encoding”或“ fileencoding”或其他任何东西-Python只是在寻找“ coding”而不考虑任何前缀)。
Alex Dupuy 2014年

7

还应考虑到unicode_literal将影响eval()但不会repr()(不对称的行为,恕我直言是一个错误),即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 2014年

5

还有更多。

有一些库和内建函数期望不容许unicode的字符串。

两个例子:

内置:

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

(略带色情)不适用于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()的调用(后者是我的),那么后者将是毁灭性的。

ang,是吗?!?


3
而且类型的东西也会泄漏到元类中-因此在Django中,您声明的任何字符串都class Meta:应该是b'field_name'
Hamish Downer

2
是的...在我的情况下,我意识到搜索和用b'版本替换所有sendMessage字符串是值得的。如果要避免可怕的“解码”异常,则没有什么比在程序中严格使用unicode,在必要时对输入和输出进行转换(我在该主题中阅读的某些论文中提到的“ unicode三明治”)更是如此。总体而言,unicode_literals对我来说是一个巨大的胜利……
GreenAsJade 2013年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.