К сожалению, поведение интерпретатора CPython в версиях, предшествующих 3.3, зависит от того, построен ли он с «узкой» или «широкой» поддержкой Unicode. Таким образом, один и тот же код, такой как вызов len
, может иметь разные результаты в разных сборках стандартного интерпретатора. См. этот вопрос для примеров.
Различие между «узким» и «широким» заключается в том, что «узкие» интерпретаторы внутренне хранят 16-битные кодовые единицы (UCS-2), тогда как «широкие» интерпретаторы внутренне хранят 32-битные кодовые единицы (UCS-4). Код точек U + 10000 и выше (вне базовой многоязычной плоскости) имеют len
из двух на «узких» интерпретаторах, потому что для представления двух кодов UCS-2 единиц их (используя суррогаты), и это то, что len
измеряет. В "широких" сборках только один код UCS-4 требуется единица для не-BMP кода точка , поэтому для этих сборок len
- один для таких кодовых точек.
Я подтвердил, что ниже обрабатывает все строки unicode
, независимо от того, содержат они суррогатные пары или нет, и работает в CPython 2.7 как с узкими, так и с широкими сборками. (Возможно, указание строки типа u'\ud83d\udc4d'
в широком интерпретаторе отражает утвердительное желание представить полный суррогатный код точка в отличие от кода с частичным символом единица и, следовательно, не является автоматически ошибка, которая будет исправлена, но я игнорирую это здесь. Это крайний случай и обычно не желательный вариант использования.)
Используемый ниже трюк @invoke
позволяет избежать повторных вычислений, не добавляя ничего к __dict__
.
модуля.
invoke = lambda f: f() # trick taken from AJAX frameworks
@invoke
def codepoint_count():
testlength = len(u'\U00010000') # pre-compute once
assert (testlength == 1) or (testlength == 2)
if testlength == 1:
def closure(data): # count function for "wide" interpreter
u'returns the number of Unicode code points in a unicode string'
return len(data.encode('UTF-16BE').decode('UTF-16BE'))
else:
def is_surrogate(c):
ordc = ord(c)
return (ordc >= 55296) and (ordc < 56320)
def closure(data): # count function for "narrow" interpreter
u'returns the number of Unicode code points in a unicode string'
return len(data) - len(filter(is_surrogate, data))
return closure
assert codepoint_count(u'hello \U0001f44d') == 7
assert codepoint_count(u'hello \ud83d\udc4d') == 7