1
0
mirror of https://github.com/satwikkansal/wtfpython synced 2024-11-24 20:14:23 +01:00

Translate Let's make a giant string! example

This commit is contained in:
Vadim Nifadev 2024-04-24 16:16:10 +03:00
parent ebe5f59a9d
commit 00e420fc51

View File

@ -3430,6 +3430,103 @@ def square(x):
``` ```
#### 💡 Объяснение: #### 💡 Объяснение:
+ Операнд `+=` быстре `+` для "сложения" 2 и более строк, так как первая строка (например, `s1` for `s1 += s2 + s3`) не уничтожается во время формирования финальной строки. + Операнд `+=` быстрее `+` для "сложения" 2 и более строк, так как первая строка (например, `s1` for `s1 += s2 + s3`) не уничтожается во время формирования финальной строки.
---
### ▶ Сделаем гигантскую строку!
<!-- Example ID: c7a07424-63fe-4504-9842-8f3d334f28fc --->
```py
def add_string_with_plus(iters):
s = ""
for i in range(iters):
s += "xyz"
assert len(s) == 3*iters
def add_bytes_with_plus(iters):
s = b""
for i in range(iters):
s += b"xyz"
assert len(s) == 3*iters
def add_string_with_format(iters):
fs = "{}"*iters
s = fs.format(*(["xyz"]*iters))
assert len(s) == 3*iters
def add_string_with_join(iters):
l = []
for i in range(iters):
l.append("xyz")
s = "".join(l)
assert len(s) == 3*iters
def convert_list_to_string(l, iters):
s = "".join(l)
assert len(s) == 3*iters
```
**Результат:**
```py
# Фрагменты выполняются в оболочке `ipython` с использованием `%timeit` для лучшей читаемости результатов.
# Вы также можете использовать модуль timeit в обычной оболочке python shell/scriptm=, пример использования ниже
# timeit.timeit('add_string_with_plus(10000)', number=1000, globals=globals())
>>> NUM_ITERS = 1000
>>> %timeit -n1000 add_string_with_plus(NUM_ITERS)
124 µs ± 4.73 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> %timeit -n1000 add_bytes_with_plus(NUM_ITERS)
211 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n1000 add_string_with_format(NUM_ITERS)
61 µs ± 2.18 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n1000 add_string_with_join(NUM_ITERS)
117 µs ± 3.21 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> l = ["xyz"]*NUM_ITERS
>>> %timeit -n1000 convert_list_to_string(l, NUM_ITERS)
10.1 µs ± 1.06 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
```
Увеличим число итераций в 10 раз.
```py
>>> NUM_ITERS = 10000
>>> %timeit -n1000 add_string_with_plus(NUM_ITERS) # Линейное увеличение времени выполнения
1.26 ms ± 76.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n1000 add_bytes_with_plus(NUM_ITERS) # Квадратичное увеличение
6.82 ms ± 134 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n1000 add_string_with_format(NUM_ITERS) # Линейное увеличение
645 µs ± 24.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n1000 add_string_with_join(NUM_ITERS) # Линейное увеличение
1.17 ms ± 7.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> l = ["xyz"]*NUM_ITERS
>>> %timeit -n1000 convert_list_to_string(l, NUM_ITERS) # Линейное увеличение
86.3 µs ± 2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
```
#### 💡 Объяснение
- Подробнее о [timeit](https://docs.python.org/3/library/timeit.html) или [%timeit](https://ipython.org/ipython-doc/dev/interactive/magics.html#magic-timeit) вы можете прочитать по этим ссылкам. Они используются для измерения времени выполнения фрагментов кода.
- Не используйте `+` для генерации длинных строк - В Python `str` неизменяема, поэтому левая и правая строки должны быть скопированы в новую строку для каждой пары конкатенаций. Если вы конкатенируете четыре строки длины 10, то вместо 40 символов вы скопируете (10+10) + ((10+10)+10) + (((10+10)+10)+10) = 90 символов. С увеличением количества и размера строки ситуация ухудшается в квадратичной прогрессии (что подтверждается временем выполнения функции `add_bytes_with_plus`).
- Поэтому рекомендуется использовать синтаксис `.format.` или `%` (правда, для очень коротких строк они немного медленнее, чем `+`).
- Или лучше, если у вас уже есть содержимое в виде итерируемого объекта, тогда используйте `''.join(iterable_object)`, что гораздо быстрее.
- В отличие от `add_bytes_with_plus` из-за оптимизаций `+=`, рассмотренных в предыдущем примере, `add_string_with_plus` не показало квадратичного увеличения времени выполнения. Если бы оператор был `s = s + "x" + "y" + "z"` вместо `s += "xyz"`, увеличение было бы квадратичным.
```py
def add_string_with_plus(iters):
s = ""
for i in range(iters):
s = s + "x" + "y" + "z"
assert len(s) == 3*iters
>>> %timeit -n100 add_string_with_plus(1000)
388 µs ± 22.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n100 add_string_with_plus(10000) # Quadratic increase in execution time
9 ms ± 298 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
```
- Такое число способов форматирования и создания гигантской строки несколько противоречит [Zen of Python](https://www.python.org/dev/peps/pep-0020/), согласно которому,
> должен быть один - и желательно только один - очевидный способ сделать это.
--- ---