对列表每个元素应用无返回值的方法

11 投票
2 回答
7809 浏览
提问于 2025-04-16 16:18

有没有办法在列表推导式中使用像 random.shuffle 这样没有返回值的方法呢?

>>> import pprint
>>> import random
>>> 
>>> L = [ random.shuffle(range(5)) for x in range(5)]
>>> 
>>> print L
[None, None, None, None, None]

这是一个 for 循环,它对我列表中的每个项目应用 random.shuffle 方法:

>>> L = [ range(5) for x in range(5) ]
>>> pprint.pprint(L)
[[0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4]]
>>> for element in L:
...     random.shuffle(element)
... 
>>> pprint.pprint(L)
[[2, 0, 3, 1, 4],
 [2, 0, 1, 4, 3],
 [4, 1, 3, 0, 2],
 [1, 2, 4, 3, 0],
 [1, 3, 0, 2, 4]]

我可以使用 map,这样会打乱原来的列表,但返回的却是一个全是 None 的列表。

>>> L = [ range(5) for x in range(5) ]
>>> pprint.pprint(L)
[[0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4]]
>>> map(random.shuffle, L)
[None, None, None, None, None]
>>> pprint.pprint(L)
[[3, 0, 4, 1, 2],
 [2, 3, 0, 1, 4],
 [2, 3, 1, 4, 0],
 [4, 2, 0, 3, 1],
 [1, 3, 0, 2, 4]]

使用列表推导式配合 shuffle 也是这样:

>>> L = [ range(5) for x in range(5) ]
>>> pprint.pprint(L)
[[0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4],
 [0, 1, 2, 3, 4]]
>>> L1 = [ random.shuffle(x) for x in L ]
>>> pprint.pprint(L1)
[None, None, None, None, None]
>>> pprint.pprint(L)
[[1, 4, 0, 2, 3],
 [0, 4, 1, 3, 2],
 [2, 3, 4, 0, 1],
 [4, 1, 0, 2, 3],
 [2, 0, 4, 3, 1]]

很多人在 Stack Overflow 上提到,使用 map 或列表推导式来实现副作用(比如打乱列表)是不好的做法。我在想,是否有其他正确的方法可以在列表推导式中使用没有返回值的方法。

写一个方法来包装这个没有返回值的方法,是不是唯一的办法呢:

>>> def shuffled(L):
...     ret_val = L[:]
...     random.shuffle(ret_val)
...     return ret_val
... 
>>> L = [ shuffled(range(5)) for x in range(5)]
>>> pprint.pprint(L)
[[2, 1, 0, 4, 3],
 [4, 0, 3, 1, 2],
 [4, 2, 3, 0, 1],
 [1, 0, 4, 2, 3],
 [2, 4, 3, 0, 1]]
>>> 

2 个回答

8

Eli说得没错。不过我觉得可以用更简洁的方式:

import random

L = [range(5) for each in xrange(5)]
for each in L:
    random.shuffle(each)

[编辑]

另一方面,你也可以使用 random.sample()

from random import sample

xr = xrange(5)
L = [sample(xr, 5) for each in xr]
16

不可以,列表推导式是用来处理那些有返回值的函数的。这是它们的语义定义方式:

列表推导式提供了一种简洁的方法来创建列表,而不需要使用 map()、filter() 或者 lambda。用这种方式定义的列表通常比用这些方法构建的列表更清晰。每个列表推导式由一个表达式和一个 for 子句组成,后面可以跟零个或多个 for 或 if 子句。最终的结果是根据 for 和 if 子句的上下文来计算表达式,得到的一个列表。

看完这些,你应该明白“来自没有返回值的函数的列表推导式”这个说法是自相矛盾的。

对于一些“临时”的事情,直接使用 for 循环就可以了:

import random
L = []
for x in range(5):
  l = range(5)
  random.shuffle(l)
  L.append(l)

这样干净又简单。你的 shuffled 函数也很好,可以在列表推导式中使用。

撰写回答