Python 2到3迁移过程-关于Unicode的差异



由于不再支持Python2,我正在尝试将代码从Python2迁移到Python3
但是,由于两个版本之间的差异,我在迁移过程中遇到了困难。我知道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'

如您所见,在Python2中,Out[3]Out[5]相比具有不同的值,而在Python3中CCD_ 3和CCD_。

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

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

Python 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-string来自定义带有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'