Почему 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. Предполагается, что UnicodeEncodeError возникает, когда вы пытаетесь закодировать Unicode в строку.

Я думаю, что я не так далек от истины, когда предполагаю, что Hashlib молча попытался преобразовать unicode в строку.

В настоящее время. Я согласен, hashlib указал, что аргумент hashlib.md5() должен быть строкой или буфером только для чтения, что и является строкой Unicode. Но на самом деле это показывает, что на самом деле это не так: hashlib.md5() будет правильно работать со строками, и это все.

Конечно, основная проблема, которую это вызывает, заключается в том, что вы получите исключение с некоторыми строками Unicode, а не с некоторыми другими.

Что приводит меня к моим вопросам. Во-первых, есть ли у вас объяснение, почему hashlib реализует такое поведение? Во-вторых, считается ли это проблемой? В-третьих, есть ли способ исправить это, не меняя сам модуль?

Hashlib в основном является примером, есть несколько других модулей, которые ведут себя так же, когда предоставляются строки Unicode, что приводит вас к неудобной ситуации, когда ваша программа будет работать с вводом ASCII, но совершенно не работает с акцентами.


person Thomas Orozco    schedule 01.08.2011    source источник
comment
Вы должны прочитать об утиной печати. По сути, поскольку объект Unicode большую часть времени действует как строка, вы обычно можете использовать его как строку.   -  person Rafe Kettler    schedule 01.08.2011
comment
Действительно, но моя главная проблема заключалась в том, что если вы используете символы, отличные от ASCII, у вас возникает проблема, поскольку вы не можете указать .encode('utf-8') или что-то еще.   -  person Thomas Orozco    schedule 01.08.2011


Ответы (2)


Это не просто hashlib — Python 2 обрабатывает Unicode в ряде мест, пытаясь закодировать его как ascii. Это было одно из самых больших изменений, внесенных в Python 3.

В Python 3 строки имеют юникод, и они ведут себя так, как вы ожидаете: нет автоматического преобразования в байты, и вы должны кодировать их, если хотите использовать байты (например, для хеширования MD5). Я полагаю, что есть хаки, использующие sys.setdefaultencoding, которые обеспечивают такое поведение в Python 2, но я бы не советовал использовать их в рабочей среде, потому что они повлияют на любой код, работающий в этом экземпляре Python.

person Thomas K    schedule 01.08.2011
comment
Интересно, я не знал, что Python3 изменил это. Итак, вы получаете TypeErrors в Python3 всякий раз, когда используете Unicode в местах, где должны использоваться строки? Очень жаль, что так много библиотек требуют Python 2.X. - person Thomas Orozco; 01.08.2011
comment
@Thomas Orozco: Да, за исключением того, что строки являются юникодом в Python 3. Вы получаете ошибки, если пытаетесь использовать юникод вместо байтов или наоборот. Библиотечная поддержка стала немного лучше за последний год или около того. - person Thomas K; 01.08.2011
comment
Итак, «новый» юникод python3 — это строка, а то, что раньше было строкой, теперь является байтом? Получил это, спасибо за информацию. (К сожалению, я использую в основном Django, поэтому, думаю, мне просто придется подождать!) - person Thomas Orozco; 01.08.2011
comment
Да все верно. Однако не похоже, что Django будет портирован в ближайшее время. - person Thomas K; 02.08.2011
comment
Django 1.5 — первая версия Django, поддерживающая Python 3. docs.djangoproject.com/ ru/1.9/topics/python3 - person MKesper; 23.12.2015

Это результат того, что Python 2.x C API упрощает передачу объектов Unicode в C API, ожидающих строку.

См. вызов PyArg_ParseTuple* в _hashopenssl.c.

Он попытается закодировать объект Unicode в строку байтов при анализе его для аргумента 's*'. Если он не может быть закодирован, будет выдана ошибка. Правильным будет всегда вызывать .encode('utf-8') или любой другой кодек, требуемый вашим приложением, прежде чем пытаться использовать что-либо Unicode в контексте, где имеет смысл только необработанный поток байтов.

Python 3.x исправляет это. Вместо этого вы всегда получите дружественный:

TypeError: Unicode-объекты должны быть закодированы перед хешированием

Вместо любого автоматического кодирования.

person gps    schedule 22.01.2012