我想为列表的一部分分配单个值。有没有更好的解决方案,而不是以下之一?
也许性能最好,但有些丑陋:
>>> l=[0,1,2,3,4,5]
>>> for i in range(2,len(l)): l[i] = None
>>> l
[0, 1, None, None, None, None]
简洁(但我不知道Python是否认识到不需要重新排列列表元素):
>>> l=[0,1,2,3,4,5]
>>> l[2:] = [None]*(len(l)-2)
>>> l
[0, 1, None, None, None, None]
同样的警告如上:
>>> l=[0,1,2,3,4,5]
>>> l[2:] = [None for _ in range(len(l)-2)]
>>> l
[0, 1, None, None, None, None]
不确定是否使用库进行这样一项简单的任务是明智的:
>>> import itertools
>>> l=[0,1,2,3,4,5]
>>> l[2:] = itertools.repeat(None,len(l)-2)
>>> l
[0, 1, None, None, None, None]
我看到切片的赋值(对于for循环)的问题是Python可能会尝试准备“l”长度的变化。毕竟,通过插入更短/更长的切片来更改列表涉及复制列表AFAIK的所有元素(即所有引用)。如果Python也在我的情况下这样做(虽然没有必要),操作变为O(n)而不是O(1)(假设我只是总是改变一些元素)。
答案 0 :(得分:6)
时间安排:
python -mtimeit "l=[0,1,2,3,4,5]" "for i in range(2,len(l)):" " l[i] = None"
1000000 loops, best of 3: 0.669 usec per loop
python -mtimeit "l=[0,1,2,3,4,5]" "l[2:] = [None]*(len(l)-2)"
1000000 loops, best of 3: 0.419 usec per loop
python -mtimeit "l=[0,1,2,3,4,5]" "l[2:] = [None for _ in range(len(l)-2)]"
1000000 loops, best of 3: 0.655 usec per loop
python -mtimeit "l=[0,1,2,3,4,5]" "l[2:] = itertools.repeat(None,len(l)-2)"
1000000 loops, best of 3: 0.997 usec per loop
看起来l[2:] = [None]*(len(l)-2)
是您提供的最佳选项(对于您正在处理的范围)。
注意:
请记住,结果会因Python版本,操作系统,其他当前正在运行的程序而异,最重要的是 - 列表的大小和要替换的切片的大小。对于较大的范围,可能最后一个选项(使用itertools.repeat
)将是最有效的,易于阅读(pythonic)和高效(性能)。
答案 1 :(得分:5)
您的所有解决方案都是Pythonic,同样具有可读性。如果您真的关心性能并认为在这种情况下它很重要,请使用timeit
模块对它们进行基准测试。
话虽如此,我希望第一个解决方案几乎肯定不是性能最好的解决方案,因为它迭代Python中的列表元素。此外,Python不会优化远离列表在分配右侧创建的列表,但列表创建速度非常快,并且在大多数情况下,一个小的临时列表根本不会影响执行。就个人而言,对于一个简短的列表,我会选择你的第二个解决方案,对于更长的列表,我会选择itertools.repeat()
。
请注意,itertools
并不真正算作“库”,它带有Python,并且经常被使用,因此它基本上是语言的一部分。
答案 2 :(得分:1)
我建议这样的事情:
>>> l = [0, 1, 2, 3, 4, 5]
>>> n = [None] * len(l)
>>> l[2:] = n[2:]
>>> l
[0, 1, None, None, None, None]
它看起来很漂亮:没有显式循环,没有'if',没有比较!以2N的复杂性为代价! (或不?)
答案 3 :(得分:1)
更多的Pythonic和更高性能的目标有时会发生碰撞。所以你基本上要问两个问题。如果真的需要性能:测量它并采取最快的速度。在所有其他情况下,只需使用最具可读性的内容,换句话说就是最Pythonic(其他Python程序员最可读/最熟悉的内容)。
我个人认为你的第二个解决方案非常易读:
>>> l=[0,1,2,3,4,5]
>>> l[2:] = [None]*(len(l)-2)
>>> l
[0, 1, None, None, None, None]
第二行的开头立刻告诉我你正在替换列表值的特定部分。
答案 4 :(得分:1)
我认为Python中没有直接的开箱即用的功能。我喜欢你的第二种方法,但要记住,在空间和时间之间需要权衡。这是@ user4815162342推荐的非常好的读数:Python Patterns - An Optimization Anecdote。
无论如何,如果这是你最终将在你的代码中执行的操作,我认为你最好的选择是将它包装在一个辅助函数中:
def setvalues(lst, index=0, value=None):
for i in range(index, len(lst)):
lst[i] = value
>>>l=[1,2,3,4,5]
>>>setvalues(l,index=2)
>>>l
>>>[1, 2, None, None, None]
这有一些优点:
由于恕我直言,这个行动没有直接的Python未来,这是我能想象的最好的解决方法。
希望这有帮助!
答案 5 :(得分:0)
编辑 - 立即编辑原始列表。
你忘了这一个,(IMO,这个更可读)
>>> l = [i if i < 2 else None for i in range(6)]
>>> l
[0, 1, None, None, None, None]
如果需要保留,
>>> l = range(6)
>>> l
[0, 1, 2, 3, 4, 5]
>>> l[:] = [l[i] if i < 2 else None
... for i in range(len(l))]
>>> l
[0, 1, None, None, None, None]
比如说,性能比Inbar最快的方法慢大约2.5倍。