Как заменить значения по определенным индексам списка python?

Если у меня есть список:

to_modify = [5,4,3,2,1,0]

А затем объявите два других списка:

indexes = [0,1,3,5]
replacements = [0,0,0,0]

Как я могу взять элементы to_modify в качестве индекса для indexes, а затем установить соответствующие элементы в to_modify на replacements, т.е. после запуска indexes должно быть [0,0,3,0,1,0].

По-видимому, я могу сделать это через цикл for:

for ind in to_modify:
    indexes[to_modify[ind]] = replacements[ind]

Но есть ли другой способ сделать это? Могу ли я как-нибудь использовать operator.itemgetter?


person Alcott    schedule 29.08.2011    source источник
comment
В чем проблема с упомянутым вами решением цикла (единственная проблема, которую я вижу, это l содержит индексы за пределами m)? Можете ли вы дать больше контекста для проблемы?   -  person dbr    schedule 29.08.2011
comment
@dbr: я думаю, он хочет заменить значения в s, проиндексированные индексами в l, значениями из m, поэтому здесь нет проблем с выходом за рамки.   -  person machine yearning    schedule 29.08.2011
comment
он содержит индексы за пределами a, так как вы обращаетесь к [индексу], это нехорошо.   -  person steabert    schedule 29.08.2011
comment
@steabert: Хороший улов, я этого не видел, потому что не мог видеть дальше всех однобуквенных переменных.   -  person machine yearning    schedule 29.08.2011
comment
Объект среза Python не имеет ничего общего с заданным здесь вопросом.   -  person Karl Knechtel    schedule 29.08.2011


Ответы (7)


Самая большая проблема с вашим кодом в том, что он нечитаем. Правило кода Python номер один: если он нечитаем, никто не будет смотреть на него достаточно долго, чтобы извлечь из него какую-либо полезную информацию. Всегда используйте описательные имена переменных. Чуть не уловил ошибку в вашем коде, давайте посмотрим еще раз с хорошими именами, в стиле замедленного воспроизведения:

to_modify = [5,4,3,2,1,0]
indexes = [0,1,3,5]
replacements = [0,0,0,0]

for index in indexes:
    to_modify[indexes[index]] = replacements[index]
    # to_modify[indexes[index]]
    # indexes[index]
    # Yo dawg, I heard you liked indexes, so I put an index inside your indexes
    # so you can go out of bounds while you go out of bounds.

Очевидно, что когда вы используете описательные имена переменных, вы индексируете список индексов со значениями из самого себя, что в данном случае не имеет смысла.

Кроме того, при параллельном переборе двух списков мне нравится использовать функцию zip (или izip, если вы беспокоитесь о потреблении памяти, но я не из тех, кто придерживается чистоты итераций). Так что попробуйте это вместо этого.

for (index, replacement) in zip(indexes, replacements):
    to_modify[index] = replacement

Если ваша проблема заключается только в работе со списками чисел, я бы сказал, что у @steabert есть ответ, который вы искали, с помощью этого бестолкового материала. Однако вы не можете использовать последовательности или другие типы данных переменного размера в качестве элементов массивов numpy, поэтому, если в вашей переменной to_modify есть что-то подобное, вам, вероятно, лучше всего делать это с помощью цикла for.

person machine yearning    schedule 29.08.2011
comment
да, мне больше нравится zip-way, но я бы использовал i,rep для текущих значений (мне не нравится разница в одну букву, но иногда я тоже использую ее). Итак, я думаю, массив или почтовый индекс в зависимости от ситуации. - person steabert; 29.08.2011
comment
Да, я тоже использую сокращенные имена переменных, особенно в качестве значений в своих итерациях; Я просто хотел подчеркнуть, что нет ничего постыдного в использовании целого слова, если важна ясность. Дзен Python говорит, что читабельность имеет значение. - person machine yearning; 29.08.2011
comment
Добавление к комментарию машинной тоски... Zen of python - см. pep 20. - person theQuestionMan; 01.08.2017

numpy имеет массивы, которые позволяют использовать другие списки/массивы в качестве индексов:

import numpy
S=numpy.array(s)
S[a]=m
person steabert    schedule 29.08.2011
comment
+1 Ха-ха, отлично, я только что добавил это в свой пост, GJ. Но я бы поспорил, нужно это или нет. - person machine yearning; 29.08.2011
comment
ну, все спорно, даже короткие или длинные имена переменных, так как иногда короткие имена переменных вполне в порядке (если они имеют смысл в небольших функциях). Что касается решения numpy: для меня это более читабельно, а для больших массивов вы используете мощь C :), но да, действительно полезно, только если вы работаете с массивами с самого начала, так как преобразование списка в массив требует времени. - person steabert; 29.08.2011
comment
Я полностью согласен, это зависело бы от того, почему он это делал в первую очередь... Но что, если он даже не работает с массивами чисел, а у него есть список диктов или что-то в этом роде? Ни за что, Хосе. Его проблема явно занижена. - person machine yearning; 29.08.2011
comment
Лол, я думаю, что нас забанили... см. решение @eph. - person machine yearning; 29.08.2011

Почему бы просто не:

map(s.__setitem__, a, m)
person eph    schedule 29.08.2011
comment
+1 Довольно элегантно, но некрасиво и не мейнстримно. Приятно видеть, что это пришло так поздно. - person machine yearning; 29.08.2011
comment
хорошо, но тогда у вас все еще есть итератор;) - person steabert; 29.08.2011
comment
@steabert: да, но умно, что вы не используете результат этой функции; это как бы нарушает принцип функционального программирования отсутствия побочных эффектов, что иронично, потому что map сам по себе является частью парадигмы функционального программирования Python. - person machine yearning; 30.08.2011
comment
@machine тоска: Спасибо за совет. Я никогда не рассматривал побочные эффекты map. Иногда я просто воспринимаю его как эквивалент Array.forEach, который не имеет реализации на Python. - person eph; 30.08.2011
comment
@eph: да, если вы не знали, побочный эффект - это просто слово, используемое для чего-то, что делает функция, не требующая возвращаемого значения ... поэтому даже такие вещи, как ввод/вывод, всегда основаны на побочных эффектах. . не обязательно плохо, это просто часть функционального программирования en.wikipedia.org/wiki/Functional_programming парадигма стараться избегать их, насколько это возможно; все, что делает ваша функция, должно быть зафиксировано в ее возвращаемом значении. :) - person machine yearning; 30.08.2011

Немного медленнее, но читаемо, я думаю:

>>> s, l, m
([5, 4, 3, 2, 1, 0], [0, 1, 3, 5], [0, 0, 0, 0])
>>> d = dict(zip(l, m))
>>> d  #dict is better then using two list i think
{0: 0, 1: 0, 3: 0, 5: 0}
>>> [d.get(i, j) for i, j in enumerate(s)]
[0, 0, 3, 0, 1, 0]
person utdemir    schedule 29.08.2011

Вы можете использовать operator.setitem.

from operator import setitem
a = [5, 4, 3, 2, 1, 0]
ell = [0, 1, 3, 5]
m = [0, 0, 0, 0]
for b, c in zip(ell, m):
    setitem(a, b, c)
>>> a
[0, 0, 3, 0, 1, 0]

Является ли оно более читабельным или эффективным, чем ваше решение? Я не уверена!

person Praveen Gollakota    schedule 29.08.2011
comment
Не более читабельно, потому что вы снова использовали бесполезные имена переменных, лол! Это Python, а не Haskell - person machine yearning; 29.08.2011

для индекса в:

Это приведет к тому, что index примет значения элементов a, поэтому использование их в качестве индексов — это не то, что вам нужно. В Python мы перебираем контейнер, фактически перебирая его.

«Но подождите, — скажете вы, — для каждого из этих элементов a мне нужно работать с соответствующим элементом m. Как мне это сделать без индексов?»

Простой. Мы преобразуем a и m в список пар (элемент из a, элемент из m) и перебираем пары. Что легко сделать - просто используйте встроенную библиотечную функцию zip, как показано ниже:

for a_element, m_element in zip(a, m):
  s[a_element] = m_element

Чтобы заставить его работать так, как вы пытались это сделать, вам нужно было бы получить список индексов для повторения. Это выполнимо: например, мы можем использовать range(len(a)). Но не делай этого! Это не то, как мы делаем вещи в Python. На самом деле прямое повторение того, что вы хотите повторить, — прекрасная, освобождающая разум идея.

как насчет operator.itemgetter

Здесь не совсем актуально. Цель operator.itemgetter состоит в том, чтобы превратить акт индексации во что-то вроде функции (то, что мы называем «вызываемым»), чтобы его можно было использовать в качестве обратного вызова (например, «ключ» для сортировки или мин/макс операций). Если бы мы использовали его здесь, нам пришлось бы повторно вызывать его каждый раз в цикле, чтобы создать новый элемент получения, просто чтобы мы могли сразу же использовать его один раз и выбросить. В контексте это просто занятая работа.

person Karl Knechtel    schedule 29.08.2011

Вы можете решить это с помощью словаря

to_modify = [5,4,3,2,1,0]
indexes = [0,1,3,5]
replacements = [0,0,0,0]

dic = {}
for i in range(len(indexes)):
    dic[indexes[i]]=replacements[i]
print(dic)

for index, item in enumerate(to_modify):
    for i in indexes:
        to_modify[i]=dic[i]
print(to_modify)

Результат будет

{0: 0, 1: 0, 3: 0, 5: 0}
[0, 0, 3, 0, 1, 0]
person Akram Samarikhalaj    schedule 12.05.2019
comment
Вы можете заменить первый цикл for на dic = dict(zip(indexes, replacements)). - person Joooeey; 13.05.2019