Я молодой разраб на 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 мб, тем самым, я бы сказал, что различия занимаемой памяти у скриптов не критичны.