在列表推导中使用非原地列表方法的替代方案?

3

我知道就地修改列表方法返回None而不是被修改的列表。据我所见,这使得在列表推导式内部逻辑中使用这些方法变得不可能。

创建一个列表推导式,其成员来自于对其他列表的变异,最具Python风格的方式是什么?换句话说:这个(无效)代码行的最佳替代方案是什么:

new_list = [old_list.insert(0, "X") for old_list in list_of_old_lists]

这将导致一个由None列表组成的结果,因为list.insert()返回None
是否没有一种优雅的单行代码可以实现这个功能而不需要大量切片和连接?
上面的示例为了说明我的问题而是简单的,但实际上我想在更复杂的情况下使用它来代替多个嵌套的“for”循环。
以下是我尝试做的简化样本:
word = 'abcdefg'

variations_list = []
characters_to_insert = ['X', 'Y', 'Z']

for character in characters_to_insert:
    for position in range(len(word) + 1):
        w = list(word)
        w.insert(position, character)
        this_variation = ''.join(w)
        variations_list.append(this_variation)

for v in variations_list:
    print(v)

使用嵌套的“for”循环可以很好地解决此问题,就像这样(我的实际应用程序比此示例更复杂/冗长)。

但是,我无法使用列表推导来做同样的事情,因为“insert”方法返回None

variations_list_comprehension = [list(word).insert(position, character) for position in range(len(word) +1) for character in ['X', 'Y', 'Z']]

for v in variations_list_comprehension:
    print(v)

结果以None值的列表形式呈现,因为原地变异返回“None”。


列表推导式并不能替代for循环。 - khelwood
1
可以做到吗?可以。应该这样做吗?绝对不应该。 - TemporalWolf
原始列表只是从字符串创建的临时列表,我不担心它们的变异。这就是我想做的事情:new_list_of_strings = [''.join(list(my_string).insert(0, "X")) for my_string in string_list] - Alabaster
@Alabaster,由于您在StackOverflow上已经有一段时间没有活跃了,我想提醒您有以下选项:给帮助过您的帖子点赞,以及接受最有帮助的答案。这些都是可选的(它们是一种奖励帮助您的问题和答案的方式),我只是想提一下。 - MSeifert
显示剩余2条评论
3个回答

3

不是所有的问题都需要通过理解来解决,有时候 for 循环并不是不好的选择 - 有时甚至比使用列表推导更好,特别是当我们自然而然地避免在一行中进行过多操作时。

但如果你真的想使用列表推导来解决问题,我建议你使用一个包装了原地操作的帮助函数:

def insert(it, index, value):
    lst = list(it)
    lst.insert(index, value)
    return lst

[''.join(insert(word, position, character)) for position in range(len(word) +1) for character in ['X', 'Y', 'Z']]

然而,要真正与您的疯狂解决方案等价,您需要交换推导式中的循环:
[''.join(insert(word, position, character)) for character in ['X', 'Y', 'Z'] for position in range(len(word) +1)]

优势在于可以将原地方法进行包装,这种方法不仅适用于此例(您可以以相同方式包装任何原地函数)。虽然有些冗长,但非常易读和可重用!
个人而言,我会使用带循环的生成器函数,因为可以用它来创建列表,但也可以按需创建项目(无需列表)。
def insert_characters_everywhere(word, chars):
    for character in characters_to_insert:
        for position in range(len(word) + 1):
            lst = list(word)
            lst.insert(position, character)
            yield ''.join(lst)

list(insert_characters_everywhere('abcdefg', ['X', 'Y', 'Z']))

3

如果你不关心改变另一个列表的结果,那么你不需要使用中间列表:

variations_list = [word[:i] + char + word[i:] 
                   for char in characters_to_insert 
                   for i in range(len(word) + 1)]

['Xabcdefg', 'aXbcdefg', 'abXcdefg', 'abcXdefg', 'abcdXefg', 'abcdeXfg', 'abcdefXg', 'abcdefgX', 
 'Yabcdefg', 'aYbcdefg', 'abYcdefg', 'abcYdefg', 'abcdYefg', 'abcdeYfg', 'abcdefYg', 'abcdefgY', 
 'Zabcdefg', 'aZbcdefg', 'abZcdefg', 'abcZdefg', 'abcdZefg', 'abcdeZfg', 'abcdefZg', 'abcdefgZ']

我认为这个理解最多只能算得上勉强:使用 for 循环更容易理解。

虽然我喜欢这个解决方案(+1),但问题要求一个“优雅的单行代码方法来完成这个任务,不需要大量切片和连接字符串?”- 强调是我的。 - MSeifert
公平地说,我想这取决于你认为“很多”是什么。从技术上讲,您也可以通过 "".join([word[:i], char, word[i:]]) 避免这种情况,但那只是混淆了 :) - TemporalWolf

0

我认为理解你实际想要达到的目标非常重要。

new_list = [old_list.insert(0, "X") for old_list in list_of_old_lists]
情况1: 在您上面的代码中,您是否试图创建一个新的new_list,其中包含已更新以将'X'字符作为其第一个元素的旧(!!!)列表? 如果是这种情况,则list_of_old_lists将是“新列表”的列表。

例如,

list_of_old_lists = [['A'], ['B']]
new_list = []
for old_list in list_of_old_lists:
    old_list.insert(0, 'X')
    new_list.append(old_list)
print(list_of_old_lists)
print(new_list)
print(list_of_old_lists == new_list)

将会打印:

[['X', 'A'], ['X', 'B']]
[['X', 'A'], ['X', 'B']]
True

那就是,new_listlist_of_old_lists 的浅拷贝,包含“更新”的列表。如果这正是你想要的,那么你可以使用列表推导式来实现类似以下的操作:
[old_list.insert(0, "X") for old_list in list_of_old_lists]
new_list = list_of_old_lists[:]

在我上面的例子中,您可以使用for循环的替代方法。

情况2:或者,您是否正在尝试创建一个包含更新列表的新列表,同时让list_of_old_lists保留原始列表?在这种情况下,您可以按照以下方式使用列表推导式:

new_list = [['X'] + old_list for old_list in list_of_old_lists]

然后:

In [14]:     list_of_old_lists = [['A'], ['B']]
    ...:     new_list = [['X'] + old_list for old_list in list_of_old_lists]
    ...:     print(list_of_old_lists)
    ...:     print(new_list)
    ...:     print(new_list == list_of_old_lists)
    ...: 
[['A'], ['B']]
[['X', 'A'], ['X', 'B']]
False

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