Ответ 1
В режиме tl; dr:
Это потому, что существуют две функции для двух разных случаев, как это видно в unicode_strip
; do_strip
и _PyUnicodeXStrip
первый выполняется намного быстрее, чем второй.
Функция do_strip
для общего случая str.strip()
, где аргументов нет, и do_argstrip
(который обертывает _PyUnicode_XStrip
) для случая, когда вызывается str.strip(arg)
, то есть аргументы предоставляются.
do_argstrip
просто проверяет разделитель, и если он действителен и не равен None
(в этом случае он вызывает do_strip
), он вызывает _PyUnicode_XStrip
.
Оба do_strip
и _PyUnicode_XStrip
следуют одной и той же логике, используются два счетчика, один равный нулю, а другой - длина строки.
Используя две циклы while
, первый счетчик увеличивается до тех пор, пока не будет достигнуто значение, не равное разделителю, и второй счетчик будет уменьшен до тех пор, пока не будет выполнено одно и то же условие.
Разница заключается в том, как проверять, не совпадает ли текущий символ с разделителем.
Для do_strip
:
В наиболее распространенном случае, когда символы в разделяемой строке могут быть представлены в ascii
, присутствует дополнительный небольшой прирост производительности.
while (i < len) {
Py_UCS1 ch = data[i];
if (!_Py_ascii_whitespace[ch])
break;
i++;
}
- Доступ к текущему символу в данных выполняется быстро, путем обращения к базовому массиву:
Py_UCS1 ch = data[i];
- Проверка, является ли символ белым пространством, производится простым индексом массива в массив, называемый
_Py_ascii_whitespace[ch]
.
Итак, короче говоря, он достаточно эффективен.
Если символы не находятся в диапазоне ascii
, различия не настолько резкие, но они замедляют общее выполнение:
while (i < len) {
Py_UCS4 ch = PyUnicode_READ(kind, data, i);
if (!Py_UNICODE_ISSPACE(ch))
break;
i++;
}
- Доступ осуществляется с помощью
Py_UCS4 ch = PyUnicode_READ(kind, data, i);
- Проверка, является ли символ пробелом, выполняется макросом
Py_UNICODE_ISSPACE(ch)
(который просто вызывает другой макрос:Py_ISSPACE
)
Для _PyUnicodeXStrip
:
В этом случае доступ к базовым данным осуществляется, как и в предыдущем случае, с помощью PyUnicode_Read
; проверка, с другой стороны, чтобы увидеть, является ли символ белым пространством (или действительно, любым персонажем, который мы предоставили), является немного более сложным.
while (i < len) {
Py_UCS4 ch = PyUnicode_READ(kind, data, i);
if (!BLOOM(sepmask, ch))
break;
if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0)
break;
i++;
}
PyUnicode_FindChar
, который, хотя и эффективен, намного сложнее и медленнее по сравнению с доступом к массиву. Для каждого символа в строке вызывается, чтобы увидеть, содержится ли этот символ в разделителях (-ах), которые мы предоставили. По мере того, как длина строки увеличивается, также возникают накладные расходы, вызываемые этой функцией непрерывно.
Для тех, кто заинтересован, PyUnicode_FindChar
после довольно некоторых проверок, в конечном итоге вызовет find_char
внутри stringlib
, который в случае, когда длина разделителей < 10
будет зацикливаться до тех пор, пока не найдет символ.
Кроме того, рассмотрите дополнительные функции, которые нужно уже вызывать, чтобы попасть сюда.
Что касается lstrip
и rstrip
, ситуация аналогична. Флаги, для которых существует режим полосания, а именно: RIGHTSTRIP
для rstrip
, LEFTSTRIP
для lstrip
и BOTHSTRIP
для strip
. Логика внутри do_strip
и _PyUnicode_XStrip
выполняется условно на основе флага.