【问题标题】:Any gotchas using unicode_literals in Python 2.6?在 Python 2.6 中使用 unicode_literals 的任何陷阱?
【发布时间】:2023-04-09 22:33:02
【问题描述】:

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

从 __future__ 导入 unicode_literals

进入我们的.py 文件(当我们修改它们时)。我想知道是否有其他人一直在这样做并且遇到了任何不明显的问题(可能是在花费大量时间调试之后)。

【问题讨论】:

    标签: python unicode python-2.6 unicode-literals


    【解决方案1】:

    我在处理 unicode 字符串时遇到的主要问题是当您将 utf-8 编码字符串与 unicode 字符串混合时。

    例如,考虑以下脚本。

    两个.py

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

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

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

    【讨论】:

    • 我建议使用decode() 解决方案而不是str()encode() 解决方案:您使用Unicode 对象的次数越多,代码就越清晰,因为您想要的是操作字符串,而不是外部隐含编码的字节数组。
    • 请修正您的术语。 when you mix utf-8 encoded strings with unicode ones UTF-8 和 Unicode 不是 2 种不同的编码; Unicode 是一种标准,UTF-8 是它定义的编码之一。
    • @Kos:我认为他的意思是将“utf-8 编码字符串”objects 与 unicode(因此解码)objects 混合。前者是str 类型,后者是unicode 类型。作为不同的对象,如果您尝试对它们进行求和/连接/插值,可能会出现问题
    • 这是否适用于python&gt;=2.6python==2.6
    【解决方案2】:

    同样在 2.6(python 2.6.5 RC1+ 之前)中,unicode 文字不能很好地配合关键字参数 (issue4978):

    例如,以下代码在没有 unicode_literals 的情况下工作,但如果使用 unicode_literals,则会因 TypeError 失败:keywords must be string

      >>> 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
    

    【讨论】:

    • 仅供参考,python 2.6.5 RC1+ 已修复此问题。
    【解决方案3】:

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

     # -*- coding: utf-8
    

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

     foo = "barré"
    

    导致如下错误:

    SyntaxError:第 198 行的文件 mumble.py 中的非 ASCII 字符“\xc3”, 但没有声明编码;见http://www.python.org/peps/pep-0263.html 详情

    【讨论】:

    • @IanMackinnon:Python 3 假定文件默认为 UTF8
    • @endolith:但是 Python 2 没有,如果您使用非 ascii 字符 即使在 cmets 中,也会出现语法错误!所以恕我直言,# -*- coding: utf-8 几乎是强制性声明,无论您是否使用unicode_literals
    • -*- 不是必需的;如果您要采用与 emacs 兼容的方式,我认为您将需要 -*- encoding: utf-8 -*-(也请参阅末尾的 -*-)。你只需要coding: utf-8(甚至=而不是: )。
    • 无论您是否from __future__ import unicode_literals,都会收到此错误。
    • Emacs 兼容性 requires # -*- coding: utf-8 -*- 与“编码”(不是“编码”或“文件编码”或其他任何东西 - Python 只寻找“编码”而不考虑任何前缀)。
    【解决方案4】:

    还要考虑到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+
    

    第二个断言恰好起作用,因为在 Python 2.7 中 repr('\xa4') 的计算结果为 u'\xa4'

    【讨论】:

    • 我觉得这里更大的问题是你使用repr 来重新生成一个对象。 repr documentation 明确指出这不是要求。在我看来,这会将repr 降级为仅对调试有用的东西。
    【解决方案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() 的调用(我的就是这样),那么后者是毁灭性的。

    该死,嗯?!?

    【讨论】:

    • 而且类型的东西也会泄漏到元类中——所以在 Django 中,你在 class Meta: 中声明的任何字符串都应该是 b'field_name'
    • 是的......在我的例子中,我意识到用 b' 版本搜索和替换所有 sendMessage 字符串是值得的。如果您想避免可怕的“解码”异常,没有什么比在您的程序中严格使用 unicode 并根据需要转换输入和输出(我在该主题上阅读的一些论文中提到的“unicode 三明治”)。总的来说,unicode_literals 对我来说是一个巨大的胜利......
    【解决方案6】:

    Click will raise unicode exceptions all over the place 如果在您使用click.echo 的位置导入了任何具有from __future__ import unicode_literals 的模块。这是一场噩梦……

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多