Python - путаница относительно того, как строки хранятся и обрабатываются в Python - PullRequest
1 голос
/ 23 марта 2019

Я пытаюсь прочитать, как работают строки в Python, и мне тяжело расшифровывать различные функции.Вот что я понимаю.Надеемся получить исправления и новые перспективы относительно того, как запомнить эти нюансы.

  • Во-первых, я знаю, что Unicode развился, чтобы приспособить несколько языков и акцентов по всему миру.Но как Python хранит строки?Если я определю s = 'hello', в какой кодировке хранится строка s?Это Юникод?Или он хранится в простых байтах?При выполнении type(s) я получил ответ как <type 'str'>.Однако, когда я сделал us = unicode(s), us был типа <type 'unicode'>.Является ли us типом str или в Python есть тип unicode?

  • Кроме того, я знаю, что для хранения места я знаю, что мы кодируем строки как байтыиспользуя функцию encode().Предположим, что bs = s.encode('utf-8', errors='ignore') вернет объект в байтах.Итак, теперь, когда я пишу bs в файл, я должен открыть файл в режиме wb?Я видел, что если открыть в режиме w, он сохраняет строку в файле как b"<content in s>".

  • Что делает функция decode ()? (Я знаю, вопрос слишком открытый.) Это как, мы применяем это к объекту байтов, и это преобразует строку внаша выбранная кодировка?Или он всегда конвертирует его обратно в последовательность Unicode?Можно ли извлечь какие-то другие идеи из следующих строк?

>>> s = 'hello'
>>> bobj = bytes(s, 'utf-8')
>>> bobj
'hello'
>>> type(bobj)
<type 'str'>
>>> bobj.decode('ascii')
u'hello'
>>> us = bobj.decode('ascii')
>>> type(us)
<type 'str'>
  • Как работает str(object)?Я прочитал, что он попытается выполнить функцию str () в описании объекта.Но как по-разному действует эта функция, скажем, на строки Unicode и обычные строки с байтовым кодом?

Заранее спасибо.

1 Ответ

2 голосов
/ 23 марта 2019

Важно: ниже описывается поведение python3.Хотя python2 имеет некоторые концептуальные сходства, открытое поведение будет другим.

В двух словах: из-за поддержки строкового объекта unicode в python3 это абстракция более высокого уровня.Это зависит от переводчика, как представить это в памяти.Таким образом, когда дело доходит до сериализации (например, записи текстового представления строки в файл), необходимо сначала явно кодировать ее в последовательность байтов, используя заданную кодировку (например, UTF-8).То же самое верно для преобразования байтов в строку, то есть декодирования.В python2 такое же поведение может быть достигнуто с использованием класса unicode, тогда как str является скорее синонимом к bytes.

Хотя это и не прямой ответ на ваш вопрос, взгляните на следующие примеры:

import sys

e = ''
print(len(e))            # 0
print(sys.getsizeof(e))  # 49

a = 'hello'
print(len(a))            # 5
print(sys.getsizeof(a))  # 54

u = 'hello平仮名'
print(len(u))                 # 8
print(sys.getsizeof(u))       # 90
print(len(u[1:]))             # 7
print(sys.getsizeof(u[1:]))   # 88
print(len(u[:-1]))            # 7
print(sys.getsizeof(u[:-1]))  # 88
print(len(u[:-2]))            # 6
print(sys.getsizeof(u[:-2]))  # 86
print(len(u[:-3]))            # 5
print(sys.getsizeof(u[:-3]))  # 54
print(len(u[:-4]))            # 4
print(sys.getsizeof(u[:-4]))  # 53

j = 'hello???'
print(len(j))                 # 8
print(sys.getsizeof(j))       # 108
print(len(j[:-1]))            # 7
print(sys.getsizeof(j[:-1]))  # 104
print(len(j[:-2]))            # 6
print(sys.getsizeof(j[:-2]))  # 100

Строки являются неизменяемыми в Python, и это дает интерпретатору преимущество при выборе способа кодирования строки на этапе создания.Давайте рассмотрим числа сверху:

  • Пустой строковый объект имеет служебную информацию в 49 байтов.
  • Строка с символами ASCII длиной 5 имеет размер 49 + 5. Т.е. в кодировке используется 1 байт на символ.
  • Строка со смешанными (ASCII + не-ASCII) символами занимает больше места в памятихотя длина по-прежнему составляет 8.
  • Разница u и u[1:] и в то же время разница u и u[:-1] равна 90 - 88 = 2 bytes.Т.е. кодирование использует 2 байта на символ.Даже при том, что префикс строки может быть закодирован с 1 байтом за символ.Это дает нам огромное преимущество в том, что мы выполняем операцию индексации с постоянным временем для строк , но мы платим дополнительными затратами памяти.
  • Объем памяти строки j еще выше.Это просто потому, что мы не можем кодировать все символы в нем, используя 2 байта на символ, поэтому интерпретатор теперь использует 4 байта на каждый символ.

Хорошо, продолжайте проверять поведение.Мы уже знаем, что интерпретатор хранит строки в четном количестве байтов на символ, чтобы дать нам O(1) доступ по индексу.Однако мы также знаем, что UTF-8 использует представление символов с переменной длиной.Давайте докажем это:

j = 'hello???'
b = j.encode('utf8')  # b'hello\xf0\x9f\x98\x8b\xf0\x9f\x98\x8b\xf0\x9f\x98\x8b'    
print(len(b))  # 17

Итак, мы можем видеть, что первые 5 символов кодируются с использованием 1 байта на символ, а остальные 3 символа кодируются с использованием (17 - 5)/3 = 4 байтов на символ.Это также объясняет, почему python использует 4 байта на символьное представление под капотом.

И наоборот, когда у нас есть последовательность байтов и decode ее в строку, интерпретатор примет решение о внутренней строкепредставление (1, 2 или 4 байта на символ) и полностью непрозрачно для программиста.Единственное, что должно быть прозрачным, - это кодирование последовательности байтов.Мы должны сказать переводчику, как обращаться с байтами.Пока мы должны позволить ему определиться с внутренним представлением строкового объекта.

...