Строки Python неизменяемы, поэтому почему s.split () возвращает список новых строк

CPython реализацию CPython кажется, что возвращаемое значение строки split() представляет собой список недавно выделенных строк. Однако, поскольку строки являются неизменными, кажется, что можно было сделать подстроки из исходной строки, указав на смещения.

Я правильно понимаю текущее поведение CPython? Есть ли причины для отказа от этой оптимизации пространства? Одна из причин, о которой я могу думать, заключается в том, что родительскую строку нельзя освободить до тех пор, пока все ее подстроки не будут.

3 Solutions collect form web for “Строки Python неизменяемы, поэтому почему s.split () возвращает список новых строк”

В текущей реализации CPython строки подсчитываются; предполагается, что строка не может содержать ссылки на другие объекты, потому что строка не является контейнером. Это означает, что сборку мусора не нужно проверять или отслеживать поверх строковых объектов (потому что они полностью покрыты подсчетом ссылок). Но на самом деле это хуже: в старых версиях Python не было трассировочного сборщика мусора ; GC был новым в версии 2.0 . До этого любой циклический мусор просто просачивался.

Компетентно реализованный алгоритм подстроки к смещению не должен формировать циклы. Поэтому теоретически циклический сборщик мусора не является обязательным условием для этого. Однако, поскольку мы делаем подсчет ссылок вместо трассировки, дочерние объекты становятся ответственными за Py_DECREF() своих родительских объектов в конце срока службы. В противном случае родительский утечка. Это означает, что вы не можете просто вытащить всю строку в свободный список, когда он достигнет конца жизни; вы должны проверить, является ли это подстрокой, а ветвление потенциально дорого . Python был исторически разработан для выполнения строковой обработки (например, Perl, но с более сильным синтаксисом), что означает создание и уничтожение множества строк. Кроме того, все имена переменных внутренне хранятся как строки, поэтому даже если пользователь не выполняет строчную обработку, интерпретатор. Замедление процесса освобождения строк даже незначительно может серьезно повлиять на производительность.

Без хрустального шара я не могу сказать вам, почему CPython делает это именно так. Однако есть некоторые причины, по которым вы можете сделать это.

Проблема в том, что небольшая строка может содержать ссылку на гораздо больший массив поддержки. Например, предположим, что я прочитал в файле журнала доступа HTTP 8 ГБ, чтобы проанализировать, какие пользовательские агенты чаще всего fp.read() к моему файлу, и я делаю это только с помощью fp.read() а затем запускаю регулярное выражение на весь файл одновременно, по одной строке за раз.

Я хочу знать о 10 самых распространенных пользовательских агентах, поэтому я держу это в списке.

Затем я хочу сделать тот же анализ для 100 других файлов, чтобы узнать, как изменились с течением времени лучшие 10 пользовательских агентов. Boom! Моя программа пытается использовать 800 ГБ памяти и убивается. Зачем? Как мне отладить это?

Java использовал этот метод обмена до Java 7, поэтому применяются те же рассуждения. См. Java 7 String – сложность подстроки и JDK-4513622: (str) сохранение подстроки поля предотвращает GC для объекта .

Также обратите внимание, что если для обмена файлами используется общая память, вам потребуется следовать указателю от строкового объекта на строковые данные. В CPython строковые данные обычно помещаются непосредственно после заголовка в память, поэтому вам не нужно следовать указателю. Это уменьшает количество требуемых распределений и уменьшает зависимость данных при чтении строк.

CPython внутренне использует NUL-завершенные строки в дополнение к хранению длины. Это очень ранний выбор дизайна, представленный с самой первой версии Python, и по-прежнему верен в последней версии.

Вы можете видеть это в Include / unicodeobject.h, где PyASCIIObject говорит «представление wchar_t (завершение с нулевой отметкой)», а PyCompactUnicodeObject говорит «представление UTF-8 (завершение с нулевой отметкой)». (Последние реализации CPython выбирают из одного из 4 типов исходных строк, в зависимости от потребностей кодирования Unicode.)

Многие модули расширения Python ожидают завершенную строку NUL. Было бы сложно реализовать подстроки в виде срезов в большую строку и сохранить низкоуровневый C API. Это невозможно, поскольку это можно сделать с помощью доступа к копированию на C-API. Или Python может потребовать, чтобы все авторы расширений использовали новый API-интерфейс, совместимый со слоями. Но эта сложность не стоит, учитывая проблемы, найденные на опыте других языков, которые реализуют ссылки на сублиты, как описано Dietrich Epp.

Я мало вижу в ответе Кевина, который применим к этому вопросу. Решение не имело ничего общего с отсутствием круговой сборки мусора перед Python 2.0 и не могло. Подрезные срезы реализуются с ациклической структурой данных. «Компетентно-реализованный» не является обязательным требованием, поскольку для превращения его в циклическую структуру данных может потребоваться порочная некомпетентность или злоба.

Кроме того, в deallocator не было бы лишних расходов на ветви. Если исходная строка была одного типа, а подстрока разрезала другой тип, диспетчер стандартного типа Python автоматически использовал бы правильный деллалокатор без дополнительных накладных расходов. Даже если бы существовала дополнительная ветка, мы знаем, что накладные расходы на ветвление в этом случае не являются «дорогостоящими». Python 3.3 (из-за PEP 393) имеет эти 4 back-end типа Unicode и решает, что делать на основе ветвления. Доступ к строкам происходит гораздо чаще, чем освобождение, поэтому любые издержки наложения из-за ветвления будут потеряны в шуме.

В основном это верно, что в CPython «имена переменных внутренне хранятся как строки». (Исключение состоит в том, что локальные переменные хранятся в виде индексов в локальном массиве.) Однако эти имена также интернированы в глобальный словарь, используя PyUnicode_InternInPlace (). Следовательно, нет накладных расходов на освобождение, поскольку эти строки не освобождаются, за исключением случаев, связанных с динамической отправкой с использованием неинтерминированных строк, например, через getattr ().

  • используя Python для генерации строкового литерала C JSON
  • Python - write () и writelines () и конкатенированные строки
  • Regex для существования некоторых слов, порядок которых не имеет значения
  • Почему str.translate быстрее в Python 3.5 по сравнению с Python 3.4?
  • Pandas удаляет части строки после указанного символа внутри фрейма данных
  • Почему «new_file + = строка + строка» намного быстрее, чем «new_file = new_file + line + string»?
  • Как кодировать и декодировать строку с Python для использования в URL-адресе?
  • Python 2.7 и 3.3.2, почему int ('0.0') не работает?
  • Python - лучший язык программирования в мире.