Ответ 1
Как уже упоминалось в комментариях, если вы используете s.__contains__("XYZ")
, вы получите результат, который больше похож на s.startswith("XYZ")
, потому что ему нужно взять тот же маршрут: поиск члена по строковому объекту, за которым следует вызов функции. Это, как правило, несколько дорого (недостаточно, чтобы вы, конечно, должны были беспокоиться). С другой стороны, когда вы выполняете "XYZ" in s
, синтаксический анализатор интерпретирует оператор и может сократить доступ члена к __contains__
(или, скорее, реализацию за ним, поскольку __contains__
сам по себе является лишь одним из способов доступа к реализация).
Вы можете получить представление об этом, посмотрев на байт-код:
>>> dis.dis('"XYZ" in s')
1 0 LOAD_CONST 0 ('XYZ')
3 LOAD_NAME 0 (s)
6 COMPARE_OP 6 (in)
9 RETURN_VALUE
>>> dis.dis('s.__contains__("XYZ")')
1 0 LOAD_NAME 0 (s)
3 LOAD_ATTR 1 (__contains__)
6 LOAD_CONST 0 ('XYZ')
9 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
12 RETURN_VALUE
Таким образом, сравнение s.__contains__("XYZ")
с s.startswith("XYZ")
приведет к более сходному результату, однако для строки вашего примера s
startswith
будет все еще медленнее.
Чтобы дойти до этого, вы можете проверить реализацию обоих. Интересно видеть, что содержит реализацию состоит в том, что он статически типизирован и просто предполагает, что аргумент является объектом unicode. Так что это довольно эффективно.
startswith
реализация, однако, является "динамическим" методом Python, который требует, чтобы реализация фактически анализировала аргументы. startswith
также поддерживает кортеж в качестве аргумента, что делает весь запуск метода более медленным: (сокращенный мной, с моими комментариями):
static PyObject * unicode_startswith(PyObject *self, PyObject *args)
{
// argument parsing
PyObject *subobj;
PyObject *substring;
Py_ssize_t start = 0;
Py_ssize_t end = PY_SSIZE_T_MAX;
int result;
if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
return NULL;
// tuple handling
if (PyTuple_Check(subobj)) {}
// unicode conversion
substring = PyUnicode_FromObject(subobj);
if (substring == NULL) {}
// actual implementation
result = tailmatch(self, substring, start, end, -1);
Py_DECREF(substring);
if (result == -1)
return NULL;
return PyBool_FromLong(result);
}
Вероятно, это большая причина, по которой startswith
работает медленнее для строк, для которых a contains
является быстрым из-за его простоты.