Как стать автором
Обновить

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

Время на прочтение4 мин
Количество просмотров9.3K

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

Теги:
Хабы:
Всего голосов 18: ↑6 и ↓12-6
Комментарии49

Публикации

Истории

Работа

Data Scientist
98 вакансий
Python разработчик
201 вакансия

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн
10 – 11 октября
HR IT & Team Lead конференция «Битва за IT-таланты»
МоскваОнлайн
25 октября
Конференция по росту продуктов EGC’24
МоскваОнлайн
7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн