为什么Python有时会将字符串升级为unicode,有时不会?

时间:2010-05-19 17:10:06

标签: python unicode

我很困惑。考虑一下这段代码的工作方式:

>>> foo = u'Émilie and Juañ are turncoats.'
>>> bar = "foo is %s" % foo
>>> bar
u'foo is \xc3\x89milie and Jua\xc3\xb1 are turncoats.'

这段代码完全不符合我的预期:

>>> try:
...     raise Exception(foo)
... except Exception as e:
...     foo2 = e
... 
>>> bar = "foo2 is %s" % foo2
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

有人可以解释这里发生了什么吗?为什么unicode数据是在普通的unicode字符串中还是存储在Exception对象中?为什么这会解决它:

>>> bar = u"foo2 is %s" % foo2
>>> bar
u'foo2 is \xc3\x89milie and Jua\xc3\xb1 are turncoats.'

我很困惑!谢谢你的帮助!

更新:我的编码伙伴兰德尔为了帮助我而增加了我的困惑!发送增援内容以解释这是如何理解的:

>>> class A:
...     def __str__(self): return "string"
...     def __unicode__(self): return "unicode"
... 
>>> "%s %s" % (u'niño', A())
u'ni\xc3\xb1o unicode'
>>> "%s %s" % (A(), u'niño')
u'string ni\xc3\xb1o'

请注意,此处参数的顺序决定了调用哪个方法!

1 个答案:

答案 0 :(得分:10)

Python Language Reference有答案:

  

如果format是Unicode对象,或者使用%s转换转换的任何对象是Unicode对象,则结果也将是Unicode对象。

foo = u'Émilie and Juañ are turncoats.'
bar = "foo is %s" % foo

这很有效,因为foounicode个对象。这会导致上述规则生效并生成Unicode字符串。

bar = "foo2 is %s" % foo2

在这种情况下,foo2Exception对象,显然不是unicode对象。因此,解释器尝试使用您的默认编码将其转换为普通str。显然,这是ascii,它不能代表那些字符并且有例外情况。

bar = u"foo2 is %s" % foo2

此处它再次起作用,因为格式字符串是unicode对象。因此,解释器也会尝试将foo2转换为unicode对象,这会成功。


关于兰德尔的问题:这也让我感到惊讶。但是,根据标准,此(为了便于阅读而重新格式化):

  

%s使用str()转换任何Python对象。如果提供的对象或格式为unicode字符串,则生成的字符串也将为unicode

如何创建这样的unicode对象不明确。所以两者都是合法的:

  • 调用__str__,解码回Unicode字符串,并将其插入输出字符串
  • 调用__unicode__并将结果直接插入输出字符串

Python解释器的混合行为确实很可怕。我认为这是标准中的一个错误。

编辑:引用Python 3.0 changelog,强调我的:

  

您认为您对二进制数据和Unicode的了解已经发生了变化。

     

[...]

     
      
  • 由于哲学的这种变化,几乎所有使用Unicode,编码或二进制数据的代码都很可能必须改变。这种变化是更好的,因为在2.x世界中,有很多错误与混合编码和未编码的文本有关。
  •