为什么Python的Hashlib不是强类型的?

4 投票
2 回答
1829 浏览
提问于 2025-04-16 22:39

Python 本来是强类型的。

比如说:'abc'['1'] 这个写法是错的,因为你应该在这里提供一个整数,而不是字符串。这样会报错,你可以根据错误信息去修正。

但是在 hashlib 这个模块里情况就不一样了。实际上,试试下面的代码:

import hashlib
hashlib.md5('abc') #Works OK        

hashlib.md5(1) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: md5() argument 1 must be string or read-only buffer, not int

hashlib.md5(u'abc') #Works, but shouldn't : this is unicode, not str.

haslib.md5(u'é')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 0: ordinal not in range(128)

当然,它不会因为 TypeError 报错,而是因为 UnicodeEncodeError。这个错误是在你尝试把 Unicode 编码成字符串时会出现的。

我觉得我的猜测没错,hashlib 在后台默默地尝试把 Unicode 转换成字符串。

现在,我同意 hashlib 指出传给 hashlib.md5() 的参数应该是字符串或者只读缓冲区,而 Unicode 字符串是符合这个要求的。但这其实说明了它并不是:hashlib.md5() 只会对字符串正常工作,其他的就不行。

当然,这样的主要问题是,有些 Unicode 字符串会导致异常,而有些则不会。

这让我有几个问题。首先,你能解释一下为什么 hashlib 会这样设计吗?其次,这算不算一个问题?最后,有没有办法在不修改模块的情况下解决这个问题?

hashlib 只是一个例子,还有其他几个模块在处理 Unicode 字符串时也会出现类似的情况,这就让人很不舒服,因为你的程序在处理 ASCII 输入时能正常工作,但在处理带重音符的字符时就完全失败了。

2 个回答

2

这是因为Python 2.x的C接口让传递Unicode对象到期待字符串的C接口变得很方便。

你可以看看在_hashopenssl.c中的PyArg_ParseTuple*调用。

当它解析's*'参数时,会尝试把Unicode对象编码成字节字符串。如果编码失败,就会出现错误。正确的做法是在使用任何Unicode内容之前,先调用.encode('utf-8')或者你应用需要的其他编码方式,特别是在只需要原始字节流的情况下。

Python 3.x解决了这个问题。你会看到一个友好的提示:

TypeError: Unicode-objects must be encoded before hashing

而不是自动进行编码。

12

不仅仅是hashlib,Python 2在很多地方处理Unicode时,会试图把它编码成ascii。这是Python 3做出的一个重要改变。

在Python 3中,字符串都是Unicode格式,表现得就像你想的那样:不会自动转换成字节,如果你想用字节(比如进行MD5哈希),你必须手动编码。我听说有一些技巧可以通过sys.setdefaultencoding在Python 2中实现这种行为,但我不建议在生产环境中使用,因为这会影响到在那个Python实例中运行的所有代码。

撰写回答