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

Jac*_*son 102 python unicode python-2.6 unicode-literals

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

from __future__ import unicode_literals

进入我们的.py文件(因为我们修改它们).我想知道是否还有其他人这样做并遇到任何非显而易见的陷阱(也许是在花了很多时间调试之后).

Kob*_*oba 101

我使用unicode字符串的主要问题是你将utf-8编码的字符串与unicode字符串混合在一起.

例如,请考虑以下脚本.

two.py

# encoding: utf-8
name = 'helló wörld from two'
Run Code Online (Sandbox Code Playgroud)

one.py

# encoding: utf-8
from __future__ import unicode_literals
import two
name = 'helló wörld from one'
print name + two.name
Run Code Online (Sandbox Code Playgroud)

运行的输出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)
Run Code Online (Sandbox Code Playgroud)

在这个例子中,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
Run Code Online (Sandbox Code Playgroud)

输出:

DEBUG: <html><body>helló wörld</body></html>
Run Code Online (Sandbox Code Playgroud)

但添加后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
Run Code Online (Sandbox Code Playgroud)

输出:

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)
Run Code Online (Sandbox Code Playgroud)

它失败,因为'DEBUG: %s'是一个unicode字符串,因此python尝试解码html.修复打印的几种方法是做print str('DEBUG: %s') % htmlprint 'DEBUG: %s' % html.decode('utf-8').

我希望这可以帮助您了解使用unicode字符串时的潜在问题.

  • 我建议使用`decode()`解决方案而不是`str()`或`encode()`解决方案:使用Unicode对象的次数越多,代码就越清晰,因为你想要的是操作字符串,而不是具有外部隐含编码的字节数组. (11认同)
  • @Kos:我认为他的意思是将"utf-8编码字符串"*对象*与unicode(因此解码)*对象*混合.前者是`str`类型,后者是类型`unicode`.作为不同的对象,如果您尝试对它们进行求和/插值,则可能会出现问题 (11认同)
  • 请修复你的术语.`当你将utf-8编码的字符串与unicode字符串混合时,UTF-8和Unicode不是2种不同的编码; Unicode是标准,UTF-8是它定义的编码之一. (8认同)

mfa*_*kas 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
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,python 2.6.5 RC1 +修复此问题. (17认同)

Jac*_*son 13

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

 # -*- coding: utf-8
Run Code Online (Sandbox Code Playgroud)

你的.py文件到第一行或第二行.否则行如:

 foo = "barré"
Run Code Online (Sandbox Code Playgroud)

导致如下错误:

SyntaxError: Non-ASCII character '\xc3' in file mumble.py on line 198,
 but no encoding declared; see http://www.python.org/peps/pep-0263.html 
 for details

  • +1.虽然你不应该在*所有情况下这样做吗? (5认同)
  • @IanMackinnon:Python 3假定默认情况下文件是UTF8 (5认同)
  • @endolith:但是Python 2没有,如果你使用非ascii chars*甚至在评论*中它会给出语法错误!所以恕我直言`# - * - 编码:utf-8`是一个几乎是强制性的声明,无论你是否使用`unicode_literals` (3认同)
  • Emacs兼容性[要求](https://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html)``# - * - coding:utf-8 - * - ``使用"编码"(不是"编码"或"文件编码"或其他任何东西 - Python只会查找"编码"而不管任何前缀). (3认同)
  • 无论你是否从__future__导入unicode_literals`,都会收到此错误. (2认同)

hvr*_*hvr 7

还要考虑到unicode_literal会影响eval()但不影响repr()(不对称行为,即imho是一个bug),即eval(repr(b'\xa4'))不等于b'\xa4'(与Python 3一样).

理想情况下,对于unicode_literalsPython {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+
Run Code Online (Sandbox Code Playgroud)

第二个断言恰好起作用,因为在Python 2.7中repr('\xa4')进行了评估u'\xa4'.

  • 我觉得这里更大的问题是您正在使用`repr`来重新生成对象。[`repr`文档](https://docs.python.org/2/library/functions.html#func-repr)明确指出,这*不是*必要条件。在我看来,这将`repr`降级为仅对调试有用的东西。 (2认同)

Gre*_*ade 5

还有更多.

有些库和内置函数需要不能容忍unicode的字符串.

两个例子:

内置:

myenum = type('Enum', (), enum)
Run Code Online (Sandbox Code Playgroud)

(略微esotic)不适用于unicode_literals:type()需要一个字符串.

图书馆:

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

不起作用:wx pubsub库需要一个字符串消息类型.

前者是深奥的,很容易修复

myenum = type(b'Enum', (), enum)
Run Code Online (Sandbox Code Playgroud)

但是如果你的代码充满了对pub.sendMessage()(我的是)的调用,后者就是毁灭性的.

哇,嗯?!?

  • 类型的东西也泄漏到元类中 - 所以在Django中你在`class Meta:`中声明的任何字符串都应该是`b'field_name'. (3认同)
  • 是的...在我的情况下,我意识到用b'版本搜索和替换所有sendMessage字符串是值得的.如果你想避免可怕的"解码"异常,没有什么比在你的程序中严格使用unicode,根据需要转换输入和输出(在我读到的一些论文中提到的"unicode三明治").总的来说,unicode_literals对我来说是个大赢家...... (2认同)