Python 2 到 3 迁移过程 - 关于 Unicode 的差异
Python 2 to 3 Migration Process - Differences Regarding Unicode
我正在尝试将我的代码从 Python2 迁移到 Python3,因为 Python2 不再受支持。
但是,由于两个版本之间的差异,我在迁移过程中遇到了困难。
我知道 Python2 曾经同时拥有字符串和 unicode 对象,而 Python3 字符串的默认存储是 unicode。
在我的代码中的某处,我将元组的十六进制表示形式存储在数据库中。
我从用户填写的表单中得到这个元组,其中一个值是 unicode 类型。
由于Python3没有string和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 Out[4]
和 Out[6]
中是相同的。
有没有办法重现 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'
我正在尝试将我的代码从 Python2 迁移到 Python3,因为 Python2 不再受支持。
但是,由于两个版本之间的差异,我在迁移过程中遇到了困难。
我知道 Python2 曾经同时拥有字符串和 unicode 对象,而 Python3 字符串的默认存储是 unicode。
在我的代码中的某处,我将元组的十六进制表示形式存储在数据库中。
我从用户填写的表单中得到这个元组,其中一个值是 unicode 类型。
由于Python3没有string和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 Out[4]
和 Out[6]
中是相同的。
有没有办法重现 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'