Я думаю, что str.startswith()
и str.endswith()
с их упомянутыми параметрами start
и end
предназначены для повышения читабельности кода и устранения сложности, которую вы получаете, разрезая.
text = "afoobarstring"
part_to_check = "bar"
position = 4
# approach 1 (startswith)
text.startswith(part_to_check, position)
# approach 2 (slicing)
text[position:position + len(part_to_check)] == part_to_check
# approach 3 (startswith and slicing)
text[position:].startswith(part_to_check)
В то время как три подхода примерно одинаковы, подход 1 легче читать и понимать, чем другие подходы (imho). В подходе 2 вы также должны вычислить конечную позицию строки, а в подходе 3 у вас есть копия строки (как в подходе 2) и тот же вызов функции, что и в подходе 1. Кроме того.
Я подозреваю, что это также приводит к лучшим результатам в отношении измерений времени:
In [1]: import timeit
In [2]: timeit.timeit('text="afoobarstring"; part_to_check = "bar"; position = 4; text.startswith(part_to_check, position)', number=10000000)
Out[2]: 1.6531553190034174
In [3]: timeit.timeit('text="afoobarstring"; part_to_check = "bar"; position = 4; text[position:position + len(part_to_check)] == part_to_check', number=10000000)
Out[3]: 1.8180583719986316
In [4]: timeit.timeit('text="afoobarstring"; part_to_check = "bar"; position = 4; text[position:].startswith(part_to_check)', number=10000000)
Out[4]: 2.349334787999396
И также я думаю, что подход 1 намного чище, также под капотом:
In [1]: import dis
In [2]: def startswith():
...: text="afoobarstring"
...: part_to_check = "bar"
...: position = 4
...: return text.startswith(part_to_check, position)
...:
In [3]: def slicing():
...: text="afoobarstring"
...: part_to_check = "bar"
...: position = 4
...: return text[position:position + len(part_to_check)] == part_to_check
...:
In [4]: def slicing_and_startswith():
...: text="afoobarstring"
...: part_to_check = "bar"
...: position = 4
...: return text[position:].startswith(part_to_check)
...:
In [5]: dis.dis(startswith)
2 0 LOAD_CONST 1 ('afoobarstring')
3 STORE_FAST 0 (text)
3 6 LOAD_CONST 2 ('bar')
9 STORE_FAST 1 (part_to_check)
4 12 LOAD_CONST 3 (4)
15 STORE_FAST 2 (position)
5 18 LOAD_FAST 0 (text)
21 LOAD_ATTR 0 (startswith)
24 LOAD_FAST 1 (part_to_check)
27 LOAD_FAST 2 (position)
30 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
33 RETURN_VALUE
In [6]: dis.dis(slicing)
2 0 LOAD_CONST 1 ('afoobarstring')
3 STORE_FAST 0 (text)
3 6 LOAD_CONST 2 ('bar')
9 STORE_FAST 1 (part_to_check)
4 12 LOAD_CONST 3 (4)
15 STORE_FAST 2 (position)
5 18 LOAD_FAST 0 (text)
21 LOAD_FAST 2 (position)
24 LOAD_FAST 2 (position)
27 LOAD_GLOBAL 0 (len)
30 LOAD_FAST 1 (part_to_check)
33 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
36 BINARY_ADD
37 BUILD_SLICE 2
40 BINARY_SUBSCR
41 LOAD_FAST 1 (part_to_check)
44 COMPARE_OP 2 (==)
47 RETURN_VALUE
In [7]: dis.dis(slicing_and_startswith)
2 0 LOAD_CONST 1 ('afoobarstring')
3 STORE_FAST 0 (text)
3 6 LOAD_CONST 2 ('bar')
9 STORE_FAST 1 (part_to_check)
4 12 LOAD_CONST 3 (4)
15 STORE_FAST 2 (position)
5 18 LOAD_FAST 0 (text)
21 LOAD_FAST 2 (position)
24 LOAD_CONST 0 (None)
27 BUILD_SLICE 2
30 BINARY_SUBSCR
31 LOAD_ATTR 0 (startswith)
34 LOAD_FAST 1 (part_to_check)
37 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
40 RETURN_VALUE
Я думаю это похоже на endswith()
-метод. Вот почему я не показывал это отдельно.