Pythonic方法确保python 2和3中的unicode

问题描述 投票:5回答:2

我正在移植一个库,以便它兼容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_literalsu前缀,但这两种解决方案都不起作用,因为输入来自客户端,而不是我的库中的文字。

python python-3.x python-2.x
2个回答
10
投票

不要重新发明兼容性层轮。使用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
    # ...

1
投票

使用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'"

请注意我们的字符串是如何被破坏的?直接来自strdocs

在没有编码或错误参数的情况下将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'字符串。可能不是你想要的。

备择方案

  1. 只需使用six.text_type。真。除非你故意与bytes互动,否则没什么好担心的。确保在投射之前检查Nones。
  2. 使用Django's force_text。如果您碰巧正在使用已经使用Django 1.x.x的项目,那么最安全的方法就是摆脱这种疯狂。
  3. 将Django的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
© www.soinside.com 2019 - 2024. All rights reserved.