交换列表中元素的更好方法是什么?

时间:2016-08-26 13:05:47

标签: python

我有一堆看起来像这样的列表:

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

我想按如下方式交换元素:

final_l = [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]

列表的大小可能会有所不同,但它们总是包含偶数个元素。

我是Python的新手,目前正在这样做:

l =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
final_l = []
for i in range(0, len(l)/2):
    final_l.append(l[2*i+1])
    final_l.append(l[2*i])

我知道这不是Pythonic并且想要使用更高效的东西。也许列表理解?

14 个答案:

答案 0 :(得分:104)

不需要复杂的逻辑,只需使用切片和步骤重新排列列表:

In [1]: l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

In [2]: l[::2], l[1::2] = l[1::2], l[::2]

In [3]: l
Out[3]: [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]

TLDR;

使用说明进行编辑

我相信大多数观众已经熟悉列表切片和多重赋值。如果你不这样做,我会尽力解释发生了什么(希望我不要让它变得更糟)。

为了理解列表切片,here已经对列表切片表示法有了很好的答案和解释。 简单地说:

a[start:end] # items start through end-1
a[start:]    # items start through the rest of the array
a[:end]      # items from the beginning through end-1
a[:]         # a copy of the whole array

There is also the step value, which can be used with any of the above:

a[start:end:step] # start through not past end, by step

让我们看看OP的要求:

 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # list l
  ^  ^  ^  ^  ^  ^  ^  ^  ^  ^
  0  1  2  3  4  5  6  7  8  9    # respective index of the elements
l[0]  l[2]  l[4]  l[6]  l[8]      # first tier : start=0, step=2
   l[1]  l[3]  l[5]  l[7]  l[9]   # second tier: start=1, step=2
-----------------------------------------------------------------------
l[1]  l[3]  l[5]  l[7]  l[9]
   l[0]  l[2]  l[4]  l[6]  l[8]   # desired output

第一层将是:l[::2] = [1, 3, 5, 7, 9] 第二层将是:l[1::2] = [2, 4, 6, 8, 10]

我们想重新分配first = second& second = first,我们可以使用多个分配,并更新原始列表:

first , second  = second , first

即:

l[::2], l[1::2] = l[1::2], l[::2]

作为旁注,要获取新列表但不改变原始l,我们可以从l分配新列表,然后执行上述操作,即:

n = l[:]  # assign n as a copy of l (without [:], n still points to l)
n[::2], n[1::2] = n[1::2], n[::2]

希望我不要把你们这些额外的解释混淆。如果有,请帮助更新我的并使其更好:-)

答案 1 :(得分:33)

这里有一个列表理解,可以解决这个问题:

In [1]: l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

In [2]: [l[i^1] for i in range(len(l))]
Out[2]: [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]

理解它的关键是以下演示如何置换列表索引:

In [3]: [i^1 for i in range(10)]
Out[3]: [1, 0, 3, 2, 5, 4, 7, 6, 9, 8]

^exclusive or运营商。所有i^1所做的就是翻转i的最低有效位,有效地将1,2与3交换为0,依此类推。

答案 2 :(得分:19)

您可以使用pairwise iteration并链接到flatten the list

WHERE verified='N' AND 
      CAST( date_of_register AS DATE) = CAST(DATE_SUB(CURDATE(), INTERVAL 3 DAY) AS DATE)

或者,您可以使用itertools.chain.from_iterable()来避免额外的解包:

>>> from itertools import chain
>>>
>>> list(chain(*zip(l[1::2], l[0::2])))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]

答案 3 :(得分:9)

最佳答案之间的基准:

Python 2.7:

('inp1 ->', 15.302665948867798) # NPE's answer 
('inp2a ->', 10.626379013061523) # alecxe's answer with chain
('inp2b ->', 9.739919185638428) # alecxe's answer with chain.from_iterable
('inp3 ->', 2.6654279232025146) # Anzel's answer

Python 3.4:

inp1 -> 7.913498195000102
inp2a -> 9.680125927000518
inp2b -> 4.728151862000232
inp3 -> 3.1804273489997286

如果你对python 2和3之间的不同表现感到好奇,原因如下:

正如你所看到的,@ NPE的答案(inp1)在python3.4中表现得更好,原因是在python3.X中range()是一个智能对象而不保留所有项目在内存中的那个范围之间就像一个列表。

  

在很多方面,range()返回的对象的行为就好像它是一个列表,但事实上并非如此。它是一个对象,当你迭代它时会返回所需序列的连续项,但它并没有真正产生列表,从而节省了空间。

这就是为什么在python 3中,当你切割范围对象时它不会返回一个列表。

# python2.7
>>> range(10)[2:5]
[2, 3, 4]
# python 3.X
>>> range(10)[2:5]
range(2, 5)

第二个重大变化是第三种方法(inp3)的表现增加。正如您所看到的,它与最后一个解决方案之间的差异已减少到约2秒(从~7秒)。原因是因为zip()函数在python3.X中它返回一个按需生成项的迭代器。并且由于chain.from_iterable()需要再次遍历项目,因此在此之前执行此操作完全是多余的(zip在python 2中执行的操作)。

代码:

from timeit import timeit


inp1 = """
[l[i^1] for i in range(len(l))]
   """
inp2a = """
list(chain(*zip(l[1::2], l[0::2])))
"""
inp2b = """
list(chain.from_iterable(zip(l[1::2], l[0::2])))
"""
inp3 = """
l[::2], l[1::2] = l[1::2], l[::2]
"""

lst = list(range(100000))
print('inp1 ->', timeit(stmt=inp1,
                        number=1000,
                        setup="l={}".format(lst)))
print('inp2a ->', timeit(stmt=inp2a,
                        number=1000,
                        setup="l={}; from itertools import chain".format(lst)))
print('inp2b ->', timeit(stmt=inp2b,
                        number=1000,
                        setup="l={}; from itertools import chain".format(lst)))
print('inp3 ->', timeit(stmt=inp3,
                        number=1000,
                        setup="l={}".format(lst)))

答案 4 :(得分:3)

另一种方法是创建嵌套列表,其中包含反转订单的对,然后使用itertools.chain.from_iterable

展平列表
>>> from itertools import chain
>>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(chain.from_iterable([[l[i+1],l[i]] for i in range(0,(len(l)-1),2)]))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
编辑:我刚刚将Kasramvd's benchmark test应用于我的解决方案,我发现此解决方案比其他顶级答案慢,所以我不建议将其用于大型列表。如果性能不重要,我仍然觉得这很可读。

答案 5 :(得分:2)

使用chainlist comprehension

的可能答案之一
>>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(chain([(l[2*i+1], l[2*i]) for i in range(0, len(l)/2)]))
[(2, 1), (4, 3), (6, 5), (8, 7), (10, 9)]

答案 6 :(得分:1)

另一种简单重新分配和切片技术的方法

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for a in range(0,len(l),2):
    l[a:a+2] = l[a-len(l)+1:a-1-len(l):-1]
print l

输出

[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]

答案 7 :(得分:1)

为了好玩,如果我们在更一般的范围内将“swap”解释为“反向”,itertools.chain.from_iterable方法可用于更长的子序列。

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

def chunk(list_, n):
    return (list_[i:i+n] for i in range(0, len(list_), n))

list(chain.from_iterable(reversed(c) for c in chunk(l, 4)))
# [4, 3, 2, 1, 8, 7, 6, 5, 10, 9]

答案 8 :(得分:1)

(另一种)替代方案:

final_l = list() # make an empty list
for i in range(len(l)): # for as many items there are in the original list
    if i % 2 == 0: # if the item is even
        final_l.append(l[i+1]) # make this item in the new list equal to the next in the original list
    else: # else, so when the item is uneven
        final_l.append(l[i-1]) # make this item in the new list equal to the previous in the original list

这假设原始列表具有偶数个项目。如果没有,可以添加try-except

final_l = list()
for i in range(len(l)):
    if i % 2 == 0:
        try: # try if we can add the next item
            final_l.append(l[i+1])
        except:  # if we can't (because i+1 doesnt exist), add the current item
            final_l.append(l[i])
    else:
            final_l.append(l[i-1])

答案 9 :(得分:0)

使用Numpy的方式

import numpy as np

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
l = np.array(l)
final_l = list(np.flip(l.reshape(len(l)//2,2), 1).flatten())

答案 10 :(得分:0)

新堆栈溢出。请随意对此解决方案发表评论或反馈。    swap = [2,1,4,3,5]

lst = []
for index in range(len(swap)):
        if index%2 == 0 and index < len(swap)-1:
            swap[index],swap[index+1]  = swap[index+1],swap[index]
        lst.append(swap[index])
print(lst)


out = [1, 2, 3, 4, 5]

答案 11 :(得分:-2)

我认为你的实施没有任何问题。但你可以做一个简单的交换。

l =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, len(l), 2):
    old = l[i]
    l[i] = l[i+1]
    l[i+1] = old

修改 显然,Python有一个更好的方法来进行交换,这将使代码像这样

l =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, len(l), 2):
    l[i], l[i+1] = l[i+1], l[i]

答案 12 :(得分:-4)

newList = [(x[2*i+1], x[2*i]) for i in range(0, len(x)/2)]

现在找到解压缩元组的方法。我不会做你所有的功课。

答案 13 :(得分:-4)

这是一个基于modulo运算符的解决方案:

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even = []
uneven = []
for i,item in enumerate(l):
    if i % 2 == 0:
        even.append(item)
    else:
        uneven.append(item)

list(itertools.chain.from_iterable(zip(uneven, even)))