首页 > 解决方案 > Python 2 到 3 迁移过程 - 关于 Unicode 的差异

问题描述

我正在尝试将我的代码从 Python2 迁移到 Python3,因为不再支持 Python2。
但是,由于两个版本之间的差异,我在迁移过程中遇到了困难。我知道 Python2 曾经有字符串和 unicode 对象,而 Python3 默认存储的字符串是 unicode。

在我的代码中的某处,我将元组的十六进制表示形式存储在数据库中。
我从一个用户填写的表单中得到这个元组,其中一个值是 unicode 类型的。
由于 Python3 没有字符串和 unicode 之间的区别,我最终得到了包含相同值的元组的不同十六进制摘要表示。

这是显示我的问题的代码片段:

Python2 -

In [1]: from hashlib import sha1

In [2]: cred = ('user', 'pass')

In [3]: sha1(str(cred)).hexdigest()
Out[3]: '7cd99ee437e8166559f55a0336d4b48d9bc62bb2'

In [4]: unicode_cred = ('user', u'pass')

In [5]: sha1(str(unicode_cred)).hexdigest()
Out[5]: '807a138ff9b0dd6ce6a937e3df3bba3223b40fcd'

Python3 -

In [1]: from hashlib import sha1                                                

In [2]: cred = ('user', 'pass')                                                 

In [3]: sha1(str(cred)).hexdigest()                                             
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-847e91fdf4c5> in <module>
----> 1 sha1(str(cred)).hexdigest()

TypeError: Unicode-objects must be encoded before hashing

In [4]: sha1(str(cred).encode('utf-8')).hexdigest()                             
Out[4]: '7cd99ee437e8166559f55a0336d4b48d9bc62bb2'

In [5]: unicode_cred = ('user', u'pass')                                        

In [6]: sha1(str(unicode_cred).encode('utf-8')).hexdigest()                     
Out[6]: '7cd99ee437e8166559f55a0336d4b48d9bc62bb2'

如您所见,在 Python2Out[3]中与 相比具有不同的值Out[5],而在 Python3Out[4]Out[6]则相同。

有没有办法重现Out[5]Python2 代码段中显示的值?
作为迁移过程的一部分,我需要确保相同的输入产生相同的输出,因此我不会在数据库中插入新记录,而是更新现有记录。

标签: pythonpython-3.xunicodepython-2.x

解决方案


使用str()输出的十六进制摘要是问题所在。 str()是一个与版本相关的字符串,您需要完全相同的表示形式来形成十六进制摘要:

蟒蛇2

>>> unicode_cred = ('user', u'pass')
>>> str(unicode_cred)
"('user', u'pass')"

Python 3(注意缺少的 'u')。的输出str()也是 Python 3 上的 Unicode 字符串,因此必须将其编码为字节才能与sha1(). 不是字符串的b一部分,只是表示它现在是一个字节字符串。

>>> unicode_cred = ('user', u'pass')
>>> str(unicode_cred).encode('utf-8')
b"('user', 'pass')"

您需要与 形成相同的字符串u以获得相同的摘要,这有点难看。在这里,我使用一个 f 字符串来自定义格式化带有u. 我也使用编码,ascii因为非 ASCII 字符会产生额外的问题。希望您没有非 ASCII 的用户名和密码。

>>> from hashlib import sha1
>>> unicode_cred = ('user', u'pass')
>>> f"('{unicode_cred[0]}', u'{unicode_cred[1]}')"
"('user', u'pass')"
>>> sha1(f"('{unicode_cred[0]}', u'{unicode_cred[1]}')".encode('ascii')).hexdigest()
'807a138ff9b0dd6ce6a937e3df3bba3223b40fcd'

推荐阅读