我正在移植一个库,以便它兼容python 2和3.库从调用应用程序接收字符串或类似字符串的对象,我需要确保这些对象转换为unicode字符串。
在python 2中,我可以这样做:
unicode_x = unicode(x)
在python 3中,我可以这样做:
unicode_x = str(x)
但是,我拥有的最佳跨版本解决方案是:
def ensure_unicode(x):
if sys.version_info < (3, 0):
return unicode(x)
return str(x)
这肯定不是很好(虽然它的工作原理)。有更好的解决方案吗?
我知道unicode_literals
和u
前缀,但这两种解决方案都不起作用,因为输入来自客户端,而不是我的库中的文字。
不要重新发明兼容性层轮。使用six
compatibility layer,这是一个小型单文件项目,可以包含在您自己的项目中:
Six支持自2.6以来的每个Python版本。它只包含在一个Python文件中,因此可以轻松地复制到项目中。 (必须保留版权和许可声明。)
它包括一个正确执行此操作的six.text_type()
callable,将值转换为Unicode文本:
import six
unicode_x = six.text_type(x)
在project source code中,这被定义为:
import sys
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
# ...
if PY3:
# ...
text_type = str
# ...
else:
# ...
text_type = unicode
# ...
使用six.text_type
几乎总是足够,就像接受的答案所说的那样。
另外,如果你以某种方式向它提供bytes
实例,你可能会在Python 3中遇到麻烦(虽然这应该很难做到)。
CONTEXT
six.text_type
基本上是Python 3中str
的别名:
>>> import six
>>> six.text_type
<class 'str'>
令人惊讶的是,使用str
来投射bytes
实例会产生一些意想不到的结果:
>>> six.text_type(b'bytestring')
"b'bytestring'"
请注意我们的字符串是如何被破坏的?直接来自str
的docs:
在没有编码或错误参数的情况下将
bytes
对象传递给str()
属于返回非正式字符串表示的第一种情况。
也就是说,str(...)
实际上会调用对象的__str__
方法,除非你通过encoding
:
>>> b'bytestring'.__str__()
"b'bytestring'"
>>> six.text_type(b'bytestring', encoding='utf-8')
'bytestring'
可悲的是,如果你传递了encoding
,“施放”常规的str
实例将不再有效:
>>> six.text_type('string', encoding='utf-8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: decoding str is not supported
在一个有点相关的说明,铸造None
值也很麻烦:
>>> six.text_type(None)
'None'
从字面上看,你最终会得到一个'None'
字符串。可能不是你想要的。
备择方案
bytes
互动,否则没什么好担心的。确保在投射之前检查None
s。force_text
。如果您碰巧正在使用已经使用Django 1.x.x的项目,那么最安全的方法就是摆脱这种疯狂。force_text
复制粘贴到您的项目中。这是一个sample implementation。对于任何一个Django替代方案,请记住,force_text
允许您指定strings_only=True
以整齐地保留None
值:
>>> force_text(None)
'None'
>>> type(force_text(None))
<class 'str'>
>>> force_text(None, strings_only=True)
>>> type(force_text(None, strings_only=True))
<class 'NoneType'>
但要小心,因为它不会投射其他几种原始类型:
>>> force_text(100)
'100'
>>> force_text(100, strings_only=True)
100
>>> force_text(True)
'True'
>>> force_text(True, strings_only=True)
True