Pull to refresh

Comments 49

Первое решение которое пришло в голову, это 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]

А в чем собственно оптимизация по памяти заключается? Это два абсолютно одинаковых выражения

JIT увидит что переменная называется _, испугается и выоптимизирует её, видимо

На сколько мне известно _ не хранит в себе данных.
Это пустая переменная.

Зависит от языка. В большинстве это просто соглашение, на которое компиляторам/оптимизаторам конечно же плевать.

Хранит, это такая же переменная как и любая другая

И что там пишут? Что [0 for i in List1] и [0 for _ in List1] разные выражения?

Там ответ на вопрос о разности i и _. Выше, указывали, что _ не хранит в себе данных. А по ответу на стаке видно полное описание _.

>Выше, указывали, что _ не хранит в себе данных

Вы бы хоть проверили прежде чем такое писать

>>> [_ for _ in range(3)]
[0, 1, 2]

То ли дело в JS:

new Array(length).fill(0)

Но да, в python умножение [0] на нужную длину — прекрасное решение.

Генератор можно было проще сделать:

result = (0 for _ in List1)

Только вот пятое решение можно применять только если в список кладётся иммутабельный объект, иначе может произойти такое:

>>> data = {'hop'}
>>> l = [data] * 3
>>> l
[{'hop'}, {'hop'}, {'hop'}]
>>> data.add('hey')
>>> l
[{'hey', 'hop'}, {'hey', 'hop'}, {'hey', 'hop'}]


Never use the characters 'l' (lowercase letter el), 'O' (uppercase letter oh), or 'I' (uppercase letter eye) as single character variable names.

Кажется, вы немного не так поняли, нужно создать список с нулями такой же длины, все равно что там лежит, ваш пример правильно будет выглядеть так:

lst = [0] * len(data)

и data можно менять сколько угодно потом, lst не поменяется

>Кажется, вы немного не так поняли, нужно создать список с нулями такой же длины,

Я не про эту задачу, а в качестве предупреждения на будущее, раз статью писал "молодой разраб на python"

UFO landed and left these words here

Я молодой разраб на python и только пришел на свою первую работу

Тем временем опытный разработчик:

from itertools import repeat
list(repeat(0, len(List1)))

Опытный разработчик как раз воспользовался бы [0 for _ in List1]

Обоснование можно? Я бы такой код не пропустил. Как минимум, потому что бесполезная итерация по List1 может дорого стоить. Как максимум, ваш вариант дольше в прочтении.

Красивые и читаемые варианты это:
>>> [0 for _ in List1]
и
>>> [0] * len(List1)

Обоснование по производительности последнего приведено в статье, теперь вы доказывайте что ваш вариант быстрее/ест меньше памяти, ибо в читаемости он проигрывает с головой

"*" это сомнительный оператор. Во-первых, потому что нужно помнить, что именно он делает. Когда программируешь сразу на нескольких языках, то такие нюансы забываются. И разве не Питона девиз "очевидное лучше неочевидного"? Во-вторых, стоит при рефакторинге сменить 0 на "0" и семантика меняется.

Если не использовать все "кажущиеся сомнительными" вещи, тогда к С лучше не приближаться вообще. Может лучше иметь разработчиков, которые знают языки, на которых пишут?

К си и правда лучше не приближаться. А риторика "язык хороший это люди с дырявой памятью плохие" - ну простите, какие есть. То что сомнительные места многих языков стоит обходить стороной знает любой миддл, и уж точно сениор, а вот джуны как раз любят блеснуть каким-нибудь эджкейсом описанным в единственном месет в пятой сноске 537 страницы стандарта. Зато потом можно всех остальных порицать "как этого можно не знать" и "ну я же говорила"

"сомнительные места" и использование оператора * для повторения списка - разные вещи. И второе, мне кажется, есть в любом гайде по питону.

P.S. Хорошо, что в нашем безумном мире ещё не все пишут новые фреймворки на js и ещё осталось кому писать драйверы на том самом C, к которому "лучше не приближаться"

"сомнительные места" и использование оператора * для повторения списка - разные вещи. И второе, мне кажется, есть в любом гайде по питону.

А при чем тут умножение для списка? Я про "все сомнительные места" отвечал.

P.S. Хорошо, что в нашем безумном мире ещё не все пишут новые фреймворки на js и ещё осталось кому писать драйверы на том самом C, к которому "лучше не приближаться"

А вот хорошо ли это - большой вопрос

А при чем тут умножение для списка? Я про "все сомнительные места" отвечал

Так вы же эти "сомнительные места" и ввели, речь шла про "сомнительный оператор *" :)

создание списка с нулями, такой же длинны, как исходный итерируемый обьект python.

итерируемый обьект может не определять __len__

кстати да, тогда решение >>> [0] * len(List1) строго говоря не верное

Но на самом деле решение [0 for _ in List1] в такой постановке задачи тоже верным назвать нельзя, ибо если List1 - это генератор, то запороть его ради создания списка нулей выглядит не самым мудрым решением.

List1 не определяет __len__? Ок. Map1 вероятно, это карта сокровищ, а не ассоциативный массив.

Я процитировал исходную постановку задачи, где речь идет про некий "итерируемый обьект python". В такой постановке задачи решение с использованием len валидно в частном случае, например, для списка List1, но не валидно в общем случае.
Автору, вероятно, следовало бы сделать уточнение по этому поводу.

>Как минимум, потому что бесполезная итерация по List1 может дорого стоить

А тут list(repeat(0, len(List1))) по вашему мнению нет бесполезной итерации?

Ну зато итерируется по итератору repeat, который той же длины что и List1. Те же O(n), где n - длина списка List1. В чем разница?

Вариант с итерациями можно обобщить на задачу построить итератор по исходной структуре той же длины, но с нулями. В таком виде вариант с итератором возполяет построить бесконечный стрим нулей, Если исходный итератор тоже бесконечный. Никакие версии проверяющие длину тут не сработают.

Интересно, а Ваш ИТ руководитель сможет справиться с задачкой правильной расстановки в тексте запятых (на примере Вашего сообщения)?
Попробуйте подкинуть ему эту тему! Заодно проверим уровень Вашего руководителя!
И это исследование будет не менее интересно, чем тема, заявленная в заголовке.
Было бы ещё совсем не лишним действием исправить пяток-другой орфографических ошибок и опечаток в тексте!
Вот еще в копилку:

List1 = [3, 4, 5]
result2 = list(map(lambda _: 0, List1))

Написали одну задачу, а обсуждаете решение другой. У типичного iterable object в вакууме не определен метод _len_ и значит половина обсуждаемых решений тут просто не работает.

В CPython 3-забыл-каком, кажется 3.5 серьёзно проапгрейдили реализацию generator expression и теперь во всех случаях нужно использовать именно его, потому что он обычно самый читаемый и теперь ещё и самый быстрый.

К функциям map / filter / reduce стоит обращаться тогда, когда нужно собрать из них целую ленивую гирлянду. Чтобы не делать гору вложенных выражений, которую трудно читать. Ну и для reduce, потому что reduce через generator expression пишется ну очень коряво, и только начиная с 3.8.

Вот результаты одного сравнения. Задача такая:

MILLION_NUMBERS = list(range(1_000_000))
def list_comprehension():
  return [number for number in MILLION_NUMBERS if not number % 2]

Результаты:

  • Через for loop - 65ms

  • List comprehension - 45ms

  • filter - 0.2ms чтобы создать iterator, да только ещё 100ms чтобы преобразовать его в лист.

А он серьёзно 150 Мб ест на такую задачу? Интересно, на что?

Вы ведь знаете образом динамические языки предоставляют собственно "динамичность"?

Догадываюсь, но интересно, сколько отъест тот же код на php или js

А вообще тут собрались душные деды учить молодых :). Статья как минимум интересная, а для новичков даже полезная.

#include "python3.6m/Python.h"
static PyObject* py_NullList (...){}

Ну вы поняли…

Не будем

>>> List1 = [3, 4, 5]

>>> [0 for _ in List1]
[0, 0, 0]

>>> list(range(len(List1)))
[0, 1, 2]


исходный итерируемый обьект python.

может не иметь длины. Вот пример:

def m():
    yield from range(5)
it = m()
len(it)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'generator' has no len()

Так что варианты с len(List1) не подходят.

Как отметили выше, неясно, в чём заключается "оптимизация по памяти" первого решения от вашего руководителя: _ - всё такая же переменная, как и любая другая (i), никаких особых правил к ней не применяется. Даже ваши собственные темты это показывают.

Sign up to leave a comment.

Articles