Python:修改列表元素的正确方法是什么?

3

我有一个包含元组的列表:

l = [('a','b'),('c','d'),('e','f')]

需要传入两个参数:一个键值和一个新值用于修改。例如:

key = 'a'
new_value= 'B' # it means, modify with 'B' the value in tuples where there's an 'a'

我有两个选项(都可以使用):

f = lambda t,k,v: t[0] == k and (k,v) or t 
new_list = [f(t,key,new_value) for t in l]
print new_list 

并且

new_list = []
for i in range(len(l)):
    elem = l.pop()
    if elem[0] == key:
        new_list.append((key,new_value))
    else:
        new_list.append(elem)
print new_list

但是,我在Python方面还很新,并不确定是否正确。

你能帮助我吗?谢谢!


5
如果您需要类似于字典的功能,请使用字典。显然,这意味着键必须是唯一的;我不知道这是否符合您的要求。 - NullUserException
谢谢,我知道字典。这是一个更一般的问题,如何修改列表中的元素。 - santiagobasulto
这不会修改a列表,它会创建一个新的副本。你想要哪个? - Kathy Van Stone
@Kathy,你说得对!我想知道两个方法。要进行修改,我会使用pop(i)和insert(i,x)。你觉得呢? - santiagobasulto
在Python中修改列表有不同的方法,因为不同的情况最适合不同的方法。没有通用的答案来确定修改列表的最佳方式。 - Winston Ewert
可能是重复的问题:如何在for循环期间修改列表条目? - Dana the Sane
4个回答

4
这里有一个解决方案,涉及到就地修改项目。
def replace(list_, key, new_value):
    for i, (current_key, current_value) in enumerate(list_):
        if current_key == key:
            list_[i] = (key, new_value)

或者,如果它没有在里面,则追加。

def replace_or_append(list_, key, new_value):
    for i, (current_key, current_value) in enumerate(list_):
        if current_key == key:
            list_[i] = (key, new_value)
            break
    else:
        list_.append((key, new_value))

使用方法:

>>> my_list = [('a', 'b'), ('c', 'd')]
>>> replace(my_list, 'a', 'B')
>>> my_list
[('a', 'B'), ('c', 'd')]

如果您想创建一个新的列表,使用列表推导式是最简单的方法。
>>> my_list = [('a', 'b'), ('c', 'd')]
>>> find_key = 'a'
>>> new_value = 'B'
>>> new_list = [(key, new_value if key == find_key else value) for key, value in my_list]
>>> new_list
[('a', 'B'), ('c', 'd')]

如果您希望在未找到的情况下追加内容,

>>> if len(new_list) == len(my_list):
...     new_list.append((find_key, new_value))

(注意我已经更改了您的变量名称,从l变为其他名称;l很容易与I1混淆,最好避免使用。PEP8 如此规定,我也赞同。)


3
要创建一个新的列表,可以使用列表推导式:
In [102]: [(key,'B' if key=='a' else val) for key,val in l]
Out[102]: [('a', 'B'), ('c', 'd'), ('e', 'f')]

要直接修改列表:

l = [('a','b'),('c','d'),('e','f')]

for i,elt in enumerate(l):
    key,val=elt
    if key=='a':
        l[i]=(key,'B')
print(l)              
# [('a', 'B'), ('c', 'd'), ('e', 'f')]

哇,我不知道还有“内联if”和枚举函数。谢谢! - santiagobasulto
@santiagobasulto:很高兴能帮忙。在Python中,“内联ifs”被称为条件表达式或三元表达式。 - unutbu

2

要修改现有列表,只需使用列表赋值,例如:

>>> l = [('a','b'),('c','d'),('e','f')]
>>> l[0] = ('a','B')
>>> print l
[('a', 'B'), ('c', 'd'), ('e', 'f')]

通常我会使用推导式来创建新的列表,例如:

[(key, new_value) if x[0] == key else x for x in l]

但是,正如第一条评论已经提到的那样,你似乎正在尝试让一个列表做一些应该使用字典的事情。


你确定你不是想说 l[0] = ('a','B') 吗? - Tadeck
确实...由于某些愚蠢的原因,我一直在考虑一个扁平化的列表,但我还是写了元组。谢谢。 - wim
非常简单的解决方案。谢谢! - santiagobasulto

2
这是我会采用的方法。
>>> l = [('a','b'),('c','d'),('e','f')]
>>> key = 'a'
>>> new_value= 'B'
>>> for pos in (index for index, (k, v) in enumerate(l) if k == key):
...     l[pos] = (key, new_value)
...     break
... else:
...     l.append((key, new_value))
... 
>>> l
[('a', 'B'), ('c', 'd'), ('e', 'f')]

这看起来非常像一个OrderedDict,它保留了键值对的顺序。你可能需要查看一下它,看看它是否符合你的需求。
编辑:用for:...break...else:替换了try:...except StopIteration:,因为这样看起来可能不太奇怪。

你确实倾向于采用复杂的解决方案! - Chris Morgan
我并不认为这很复杂。list.index 不可用,因为我们正在寻找与键在某种程度上相似的元素,而不是等于键。enumerate 很正常,我不知道除了 <generator ...>.next() 并捕获未找到时的异常之外,有什么其他惯用语来从生成器中获取第一个元素。其他替代方案要么过于冗长,要么会生成中间列表。 - SingleNegationElimination
我认为这个问题有点复杂,就像Python之禅中所说的,复杂比混乱好。当有一个简单的解决方案-使用完整的for循环而不是使用<generator>.next()并处理StopIteration异常时,它通常更好。我必须短暂地思考你的解决方案以验证它的正确性,而我可以查看我的解决方案或其他类似的已发布解决方案并立即理解正在进行的操作。在我看来,使用for-if样式的错误会更加明显。 - Chris Morgan
有趣的解决方案和讨论!谢谢! - santiagobasulto

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