为什么Python的Hashlib不是强类型的?
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 个回答
这是因为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
而不是自动进行编码。
不仅仅是hashlib,Python 2在很多地方处理Unicode时,会试图把它编码成ascii。这是Python 3做出的一个重要改变。
在Python 3中,字符串都是Unicode格式,表现得就像你想的那样:不会自动转换成字节,如果你想用字节(比如进行MD5哈希),你必须手动编码。我听说有一些技巧可以通过sys.setdefaultencoding
在Python 2中实现这种行为,但我不建议在生产环境中使用,因为这会影响到在那个Python实例中运行的所有代码。