如何替换Python列表中特定索引处的值?

54

如果我有一个列表:

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吗?


3
你提到的循环解决方案有什么问题(我唯一能看到的问题是l包含超出m边界的索引)?你能为这个问题提供更多上下文吗? - dbr
@dbr:我认为他想用m中的值替换l中索引处s的值,因此这里不存在越界问题。 - machine yearning
1
由于您正在访问a[index],所以它包含a的越界索引,这是不好的。 - steabert
@steabert:干得好,我没有看到那个问题,因为我无法看到所有的一字变量。 - machine yearning
1
“Python的切片对象”与此处所问的问题无关。 - Karl Knechtel
8个回答

55

你的代码最大的问题在于它不可读。Python 代码的第一条规则是,如果它不易读懂,就没有人会花足够的时间来获取任何有用的信息。始终使用描述性变量名称。几乎错过了你代码中的 bug,让我们再次看看它,使用良好的名称,慢动作回放风格:

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中包含了序列或其他可变大小的数据类型作为numpy数组的元素,那么最好还是使用for循环来处理。

是的,我更喜欢zip方式,但我会使用i、rep作为运行值(我不喜欢一个字母的差异,但有时也会使用它)。所以,我想根据情况使用数组或zip。 - steabert
1
是的,我也使用缩短的变量名称,特别是在我的迭代中作为值;我只是想强调,如果清晰度很重要,使用整个单词并没有什么可耻的。Python之禅说,“可读性很重要”。 - machine yearning
补充机器学习的评论... [Python之禅-参见PEP 20。] (https://www.python.org/dev/peps/pep-0020/) - theQuestionMan

36

NumPy提供了数组,允许您使用其他列表/数组作为索引:

import numpy
S=numpy.array(s)
S[a]=m

1
+1 哈哈太棒了,我正要在我的帖子中加入这个,干得好。但是我会质疑这是否必要。 - machine yearning
好的,所有的事情都是可以辩论的,就连短变量名和长变量名也是如此,因为有时候在小函数中使用有意义的短变量名是可以接受的。至于NumPy解决方案:对我来说更易读,并且对于大型数组,您正在利用C语言的优势 :) 但是,是的,只有在从开始就使用数组时才真正有用,因为将列表转换为数组需要时间。 - steabert
我完全同意,这取决于他一开始为什么要这样做...但是如果他甚至不是真正使用数字数组,而是有一个字典列表或类似的奇怪东西呢?绝对不行。他的问题显然没有明确定义。 - machine yearning
哈哈,我觉得我们被完爆了...看看 @eph 的解决方案。 - machine yearning

17

为什么不这样写:

map(s.__setitem__, a, m)

2
虽然它不太美观也不是主流,但还是很优雅的。不过很高兴看到这个功能在这么晚才加入。 - machine yearning
1
@steabert:是的,但你不使用此函数的结果很聪明;这有点违反了函数式编程的无副作用原则,这很讽刺,因为map本身就是Python函数式编程范例的一部分。 - machine yearning
@机器学习:谢谢您的建议。我从来没有考虑过map函数中的副作用。有时候我只是把它当作Array.forEach的等效函数, 但是 Python 中并没有相应的实现。 - eph
1
@eph:是的,如果你不知道的话,副作用只是一个词,用来描述函数执行时不涉及返回值的操作...所以即使像输入/输出这样的东西也总是基于副作用。这并不一定是坏事,它只是函数式编程http://en.wikipedia.org/wiki/Functional_programming范式的一部分,尽可能避免它们;你的函数所做的任何事情都应该在其返回值中体现。 :) - machine yearning
@machineyearning 函数式编程中带有副作用的方式是一种非常优雅的编程方式。您不需要完全消除可变状态,消除部分可读性会更好。请查看我对某些人的aoc解决方案的fork - Tom Huntington
显示剩余3条评论

3
您可以使用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]

它是否比你的解决方案更易读和高效?我不确定!


你又用了无用的变量名,哈哈!这是Python,不是Haskell。 - machine yearning

3
稍微慢一些,但我认为这是易读的:
>>> 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]

2

for index in a:

这将使 index 取得 a 的元素的值,所以使用它们作为索引并不是你想要的。在Python中,我们通过对容器进行迭代来进行迭代。

“但等等”,你说,“对于a中的每个元素,我需要使用m中相应的元素进行操作。如果没有索引,我该怎么办?”

简单。我们将 am 转换成一对列表 (来自 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的目的是将索引操作转换成可调用对象,以便可以将其用作回调(例如排序或min/max操作的“键”)。如果我们在这里使用它,我们每次通过循环都需要重新调用它来创建一个新的itemgetter,只是为了能够立即使用它一次并将其丢弃。在上下文中,这只是繁琐的工作。

0
elif menu.lower() == "edit":
    print ("Your games are: "+str (games))
    remove = input("Which one do you want to edit: ")
    add = input("What do you want to change it to: ")
    for i in range(len(games)) :
        if str(games[i]) == str(remove) :
            games[i] = str(add)
            break
        else :
            pass
    pass

为什么不直接这样使用呢?从删除位置直接替换,此外你还可以添加数组并使用 .sort 和 .reverse 如果需要的话。

0

你可以使用字典来解决它

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]

你可以用 dic = dict(zip(indexes, replacements)) 替换第一个 for 循环。 - Joooeey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接