我想使用列表推导来避免编写附加到某些列表的for循环。但它可以使用返回多个值的函数吗?我希望这个(简化示例)代码可以工作......
def calc(i):
a = i * 2
b = i ** 2
return a, b
steps = [1,2,3,4,5]
ay, be = [calc(s) for s in steps]
......但它没有:(
附加到每个列表的for循环有效:
def calc(i):
a = i * 2
b = i ** 2
return a, b
steps = [1,2,3,4,5]
ay, be = [],[]
for s in steps:
a, b = calc(s)
ay.append(a)
be.append(b)
有没有更好的方法,还是我坚持这个?
答案 0 :(得分:9)
将zip
与*
:
>>> ay, by = zip(*(calc(x) for x in steps))
>>> ay
(2, 4, 6, 8, 10)
>>> by
(1, 4, 9, 16, 25)
答案 1 :(得分:3)
返回迭代器的可怕的“节省空间”版本:
from itertools import tee
ay, by = [(r[i] for r in results) for i, results in enumerate(tee(map(calc, steps), 2))]
但基本上只是使用zip
,因为大部分时间都不值得丑陋。
说明:
zip(*(calc(x) for x in steps))
将(calc(x) for x in steps)
获取[(2, 1), (4, 4), (6, 9), (8, 16), (10, 25)]
的迭代器。
当你打开包装时,你会做相同的
zip((2, 1), (4, 4), (6, 9), (8, 16), (10, 25))
所以所有项目都会立即存储在内存中。证明:
def return_args(*args):
return args
return_args(*(calc(x) for x in steps))
#>>> ((2, 1), (4, 4), (6, 9), (8, 16), (10, 25))
因此,所有项目都会立即存入内存。
那么我的工作如何?
map(calc, steps)
与(calc(x) for x in steps)
(Python 3)相同。这是一个迭代器。在Python 2上,使用imap
或(calc(x) for x in steps)
。
tee(..., 2)
获取两个迭代器,用于在迭代中存储差异。如果您以锁步方式迭代,tee
将占用O(1)
个内存。如果不这样做,tee
最多可能会占用O(n)
。所以现在我们的用法让我们可以获得O(1)
内存。
enumerate
显然会将其保持在恒定的记忆中。
(r[i] for r in results)
返回一个迭代器,它从每个结果中获取i
th 项。这意味着它在这种情况下会收到一对(依次为r=(2,1)
,r=(4,4)
等)。它返回特定的迭代器。
因此,如果在锁步中迭代ay
和by
,将使用常量内存。内存使用量与迭代器之间的距离成正比。这在很多情况下都很有用(想象一下文件或文件的差异),但正如我所说的那样,大多数时候它都不值得丑陋。还有一个额外的恒定因子开销。
答案 2 :(得分:2)
你应该告诉我们什么
[calc(s) for s in xrange(5)]
确实给了你,即
[(0, 0), (2, 1), (4, 4), (6, 9), (8, 16)]
虽然它不是您想要的2个列表,但它仍然是列表列表。此外,这看起来不一样吗?
zip((0, 2, 4, 6, 8), (0, 1, 4, 9, 16))
zip
重新打包一组列表。通常它用2个较长的列表来说明,但它也适用于许多短列表。
第三步是记住fn(*[arg1,arg2, ...]) = fn(arg1,arg2, ...)
,即*
解包列表。
把它们放在一起得到hcwhsa的答案。