Python初学者被一行复杂代码弄糊涂

4 投票
5 回答
3996 浏览
提问于 2025-04-15 21:16

我大致明白这段代码的意思,它是用来生成排列的。不过,我想知道具体在返回语句中发生了什么。

def perm(l):
    sz = len(l)
    print (l)
    if sz <= 1:
        print ('sz <= 1')
        return [l]
    return [p[:i]+[l[0]]+p[i:] for i in range(sz) for p in perm(l[1:])]

5 个回答

1

看看这个:

>>> l = [1, 2, 3, 4, 5, 6]
>>> p = l[1:]
>>> p
[2, 3, 4, 5, 6]
>>> i = 3
>>> p[:i]
[2, 3, 4]
>>> p[i:]
[5, 6]
>>> p[:i]+[l[0]]+p[i:]
[2, 3, 4, 1, 5, 6]
>>> 

所以,事情是这样的,p代表的是去掉第一个元素后的列表l[1:]的所有排列组合。接下来,irange(sz),这意味着它的值会从0变化到列表l的长度。这样就会把p分成两个列表,包含所有可能的大小(比如0和sz,1和sz-1,2和sz-2,等等),然后把l的第一个元素,也就是没有被排列的那个元素,放在这两个列表之间。

4

这个返回语句使用了列表推导式。如果把它放到实际的循环里,会更容易理解:

value = []
for i in range(sz):
    # call this function using all but the first item in l
    for p in perm(l[1:]):
        # now place the first item in l between index i-1 and index i in p
        value.append(p[:i] + [l[0]] + p[i:])
return value
10

这个 return 语句返回的是一个列表推导式,里面的每个元素都是把 l 的第一个元素放到 p 的每个位置上,从第一个位置到最后一个位置。p 是一个列表的列表,它是通过递归调用 perm 得到的,这个调用排除了 l 的第一个元素,因此会对所有其他元素进行各种可能的排列。

如果你不理解递归,这个概念确实不容易解释;-)。不过,如果你不理解列表推导式,那就简单多了——这个 return 的意思其实可以用下面的方式来表达:

result = []
for i in range(sz):
  for p in perm(l[1:]):
    result.append(p[:i]+[l[0]]+p[i:])
return result

这也显示了这段代码的低效:它递归调用 perm 总共 sz 次,显然这样做是没有必要的。更好的做法是直接交换两个 for 循环的位置:

result = []
for p in perm(l[1:]):
  for i in range(sz):
    result.append(p[:i]+[l[0]]+p[i:])
return result

而这个更好的代码的等价形式,就是把两个 for 子句的位置互换的列表推导式:

return [p[:i]+[l[0]]+p[i:] for p in perm(l[1:]) for i in range(sz)]

撰写回答