在Python中可以在列表推导式中调用函数吗(非内置函数)?

3 投票
5 回答
730 浏览
提问于 2025-04-18 18:18

假设我们有一个列表:

X = [1,2,3,4,5,6,7,8]

我们还创建了一个叫做 add() 的函数:

def add():
  sum = 0
  result = 0
  for e in X:
    sum = sum + e
    return sum
add()

这个函数会遍历一个数字列表 X,把列表中的下一个元素加到之前的总和上。所以对于每个元素 X[i],我们有:

1
3
6
10
15
21
28
36
45

现在,如果我想把这些结果再放到一个列表里,利用列表推导式来实现。这种情况下,我能在列表推导式中调用像 add() 这样的函数吗?因为在列表推导式中使用内置函数是可以的。

我试过以下代码:

L = [add() for e in X]
print L

结果是

[None, None, None, None, None, None, None, None, None]

而我期望得到的是

[1,3,6,10,15,21,28,36,45]

为什么我在这个列表中得到的是 NoneType 的值呢?

5 个回答

0

是的,你可以在列表推导式中调用任何函数。

另外要注意,在Python 2.x版本中,你不能在列表推导式里使用print,因为在那个版本里,print并不是一个函数

关于你的例子,它可以这样写,使用一个add()函数,这个函数会和共享的状态(s变量)一起工作:

s = 0   # better not use name 'sum' as it is already a builtin function
def add(i):
    global s
    s += i
    return s
X = [1, 2, 3, 4, 5, 6, 7, 8]
print [add(i) for i in X]
# prints [1, 3, 6, 10, 15, 21, 28, 36]
# but beware the global shared state! for example when called again:
print [add(i) for i in X]
# prints [37, 39, 42, 46, 51, 57, 64, 72]

你可以看看其他的回答,了解如何在没有共享状态的情况下做到这一点,这样当你忘记把s = 0时,就不会得到不同的结果。

0
#!/usr/bin/env python
"""
produce a list that adds each item to the previous item
this list [1,2,3,4] will produce this list [1,3,6,10]
"""

def accumulate(my_list, previous = 0):
    for i in my_list:
        previous += i
        yield previous

x = [1,2,3,4,5,6,7,8,9]
new_list = []
new_list = [i for i in accumulate(x)]
print x   
print new_list

这段代码会产生这样的结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 3, 6, 10, 15, 21, 28, 36, 45]
2

你的方法行不通,因为你的 add() 函数没有状态。你需要一个能够在多次调用 add() 之间保持状态的东西,否则 add() 每次都会产生相同的结果。

实现你想要的一个解决方案是 itertools.accumulate()。你可以查看 在Python中等同于Haskell的scanl 的讨论。

这里其他的回答建议使用一个包含 range() 的列表推导式。虽然这样可以实现,但效率不高,因为它是一个O(n^2)的算法,每次都要从头计算累积和。

2

是的,在列表推导式里面调用函数是可以的。你给的例子没问题,问题出在 add() 这个函数上。你需要让 add() 函数接收一个参数,也就是要计算总和的列表。

def add(elements):
    sum = 0
    for el in elements:
        sum += el
    return sum

这样,列表推导式就会变成这样:

L = [add(X[:i+1]) for i in xrange(len(X))]
[1, 3, 6, 10, 15, 21, 28, 36]

这和下面的写法是一样的:

L = [add(X[:1]), add(X[:2]), ..., add(X[:8])]

结果会是一个前缀和的列表,也就是你想要的东西。

5

你可以使用 yield 这个关键词,来保持你原来的格式:

    X = [1,2,3,4,5,6,7,8]

    def add():
      sum = 0
      for e in X:
        sum = sum + e
        yield sum

    L = [value for value in add()]
    print L

撰写回答