Задачка Python на синтаксический сахар

Kate

Administrator
Команда форума
Я молодой разраб на python и только пришел на свою первую работу. На работе руководитель ИТ отдела иногда задает задачки python нам(разрабам), одна из них - это создание списка с нулями, такой же длины, как исходный итерируемый обьект python. Итерируемый обьект может быть как списком, так и строкой, так и любым другим кастомным итерируемым обьектом.
Первое решение которое пришло в голову, это list comprehension:
#t1.py
List1 = [3, 4, 5]
result = [0 for i in List1]
Руководитель сказал, да, это python way, но можно оптимизировать по памяти:
#t2.py
List1 = [3, 4, 5]
result = [0 for _ in List1]
У меня в голове вертелось чувство что можно решить эту же задачу через рапаковку, но если Вы хотите оптимизирват по памяти, напишите генератор(но я не уверен), вот так:
#t3.py
List1 = [3, 4, 5]

def m(N):
N = len(N)
while N > 0:
yield 0
N -= 1

result = list(m(List1))
Через пару минут мысли в голове собрались в пазл и я выдал решение этой задачи через распаковку:
#t4.py
List1=[3, 4, 5]
result = list(map(int, [*'0'*len(List1)]))
Не смотря на 3 найденых решения, руководителя все же хотелось удивить и в конце вечера, из глубин сознания, я вспомнил что можно просто умножить список из одного элемента на N и получить список из N элементов:
#t5.py
List1 = [3, 4, 5]
result = [0]*len(List1)
Тест времени выполнения
Давайте теперь протеституем все эти скрипты. List1 будет список из миллиона чисел, запускать тест времени будем строеным модулем cProfile, а памяти memory_profiler.
sergey@ideapad-320S:~/test_speed$ python3 -m cProfile t1.py
4 function calls in 0.028 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.010 0.010 0.028 0.028 t1.py:1(<module>)
1 0.018 0.018 0.018 0.018 t1.py:2(<listcomp>)
1 0.000 0.000 0.028 0.028 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}

sergey@ideapad-320S:~/test_speed$ python3 -m cProfile t2.py
4 function calls in 0.029 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.010 0.010 0.029 0.029 t2.py:1(<module>)
1 0.019 0.019 0.019 0.019 t2.py:2(<listcomp>)
1 0.000 0.000 0.029 0.029 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}


sergey@ideapad-320S:~/test_speed$ python3 -m cProfile t3.py
1000005 function calls in 0.179 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.079 0.079 0.179 0.179 t3.py:1(<module>)
1000001 0.100 0.000 0.100 0.000 t3.py:3(m)
1 0.000 0.000 0.179 0.179 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {built-in method builtins.len}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}

sergey@ideapad-320S:~/test_speed$ python3 -m cProfile t4.py
4 function calls in 0.088 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.088 0.088 0.088 0.088 t4.py:1(<module>)
1 0.000 0.000 0.088 0.088 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {built-in method builtins.len}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}

sergey@ideapad-320S:~/test_speed$ python3 -m cProfile t5.py
4 function calls in 0.011 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.011 0.011 0.011 0.011 t5.py:1(<module>)
1 0.000 0.000 0.011 0.011 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {built-in method builtins.len}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}

Если запускать несколько раз тест времени, значения остаются такими же.
Дольше всего оказался генератор, ну оно и понятно, его еще создать нужно, и к нему постоянно обращятся нужно.
Первое и второе решение по скорости отличаются не на много, интересно что там с памятью.
Решение с распаковкой отличается в 3 раза от Python way решений.
И быстрее всего, прям в два раза относительно первых двух, сравниваем с ними, т.к. это решения Python way, оказалость пятое решение.
Тест памяти
Давайте теперь протеституем эти решения на память.
sergey@ideapad-320S:~/test_speed$ python3 -m memory_profiler t1.py
Filename: t1.py

Line # Mem usage Increment Occurences Line Contents
============================================================
3 154.770 MiB 154.770 MiB 1 @profile
4 def k():
5 154.770 MiB 0.000 MiB 1000003 result = [0 for i in a]
sergey@ideapad-320S:~/test_speed$ python3 -m memory_profiler t2.py
Filename: t2.py

Line # Mem usage Increment Occurences Line Contents
============================================================
3 154.648 MiB 154.648 MiB 1 @profile
4 def k():
5 154.648 MiB 0.000 MiB 1000003 result = [0 for _ in a]

sergey@ideapad-320S:~/test_speed$ python3 -m memory_profiler t3.py
Filename: t3.py

Line # Mem usage Increment Occurences Line Contents
============================================================
11 154.586 MiB 154.586 MiB 1 @profile
12 def k():
13 154.586 MiB 0.000 MiB 1 result = list(m(a))

sergey@ideapad-320S:~/test_speed$ python3 -m memory_profiler t4.py
Filename: t4.py

Line # Mem usage Increment Occurences Line Contents
============================================================
3 154.527 MiB 154.527 MiB 1 @profile
4 def k():
5 154.527 MiB 0.000 MiB 1 result = list(map(int, [*'0'*len(a)]))

sergey@ideapad-320S:~/test_speed$ python3 -m memory_profiler t5.py
Filename: t5.py

Line # Mem usage Increment Occurences Line Contents
============================================================
3 154.484 MiB 154.484 MiB 1 @profile
4 def k():
5 154.484 MiB 0.000 MiB 1 result = [0]*len(a)

На самом деле, проведя тесты несколько раз, разброс у каждого решения был +- 0.2 мб, тем самым, я бы сказал, что различия занимаемой памяти у скриптов не критичны.


 
Сверху