用一行代码简化几乎重复的列表推导式

4
考虑两个列表推导式gammadelta,它们的代码几乎完全相同。不同之处在于切片列表alphabeta
gamma = [alpha[i:i+30] for i in range(0,49980,30)]
delta = [beta[i:i+30] for i in range(0,49980,30)]

有没有一种Pythonic的方法将此代码写成一行(例如 gamma,delta = ...)?
我还有其他几个类似的代码段,我想简化这些代码中看似冗余的部分。

@TimPietzcker 刚刚在 coldspeed 的回答下发表了同样的评论。 - Joe Iddon
1
谢谢评论!没有注意到 OP 想要 2D 列表。 - cs95
1
还可以参考https://dev59.com/VHRC5IYBdhLWcg3wCMc6和https://dev59.com/lnVC5IYBdhLWcg3wYQAp。 - PM 2Ring
双重压缩? gamma, delta = zip(values[i:i+30] for i in range(0,50000,30) for values in [list(zip(alpha, beta))]) - Peter Wood
使用alpha = np.array(alpha)可以编写gamma = alpha.reshape(30, -1) - Daniel
显示剩余6条评论
4个回答

8
虽然单行列表推导式非常有用,但并不总是最佳选择。因此,因为您正在对两个列表进行相同的分块,如果您想更改分块,则必须修改这两行。
相反,我们可以使用一个函数来分块任何给定的列表,然后使用一行赋值来分块gamma和delta。
def chunk(l):
    return [l[i:i+30] for i in range(0, len(l), 30)]

gamma, delta = chunk(gamma), chunk(delta)

更直接地说,如果你想要重用它们,将它们放入一个函数中。 - Dan D.
我更喜欢这个:def chunk(l, size): return [l[i:i+size] for i in range(0, len(l), size)] - RoadRunner
@RoadRunner 是的,我正在考虑是否将其制作成通用型,可以两种方式都行 :) - Joe Iddon
是的,对于这个问题来说并不那么重要,但我仍然投了赞成票。 - RoadRunner

4

就你关于组合上述列表推导式的问题而言,你可以使用单个列表推导式zip来获取gammadelta

gamma, delta = zip(*[(alpha[i:i+30], beta[i:i+30]) for i in range(0,50000,30)])

展示如何使用zip函数的示例:

>>> zip(*[(i, i+1) for i in range(0, 10, 2)])
[(0, 2, 4, 6, 8), (1, 3, 5, 7, 9)]

在这里,我们的列表推导式将返回元组列表:

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

然后我们使用*来展开此列表,使用zip将每个可迭代对象的元素进行聚合:

>>> zip(*[(i, i+1) for i in range(0, 10, 2)])
[(0, 2, 4, 6, 8), (1, 3, 5, 7, 9)]

作为替代方案,如果需要将列表分成均匀大小的块,请参考"如何将列表分成相等大小的块?"

@Daniel,这会影响可读性吗?你是指切片还是后面的zip函数? - sunspots
@Daniel 在上面的例子中,从性能角度考虑,使用zip会比两个列表推导式表现更好。 - Moinuddin Quadri
@MoinuddinQuadri 你测试过吗?在我的机器上,单独的推导式更快。 - Dunes
@MoinuddinQuadri:你的zip变体比两个LC慢了约50%。 - Daniel
结果不太对,因为您生成的是元组而不是列表。 - Stefan Pochmann

1

只是另一种方式...

gamma, delta = ([src[i:i+30] for i in range(0,49980,30)] for src in (alpha, beta))

这比被接受的zip解决方案稍微快一些:

genny 3.439506340350704
zippy 4.3039169818228515

代码:

from timeit import timeit
alpha = list(range(60000))
beta = list(range(60000))
def genny():
    gamma, delta = ([src[i:i+30] for i in range(0,49980,30)] for src in (alpha, beta))
def zippy():
    gamma, delta = zip(*[(alpha[i:i+30], beta[i:i+30]) for i in range(0,50000,30)])
n = 1000
print('genny', timeit(genny, number=n))
print('zippy', timeit(zippy, number=n))

这是Paul Panzer在评论中提出的想法,而Peter Wood也有类似的zip想法(早在评论中)。 - sunspots
我之前没有看到过这个计时部分,它是一个不错的补充。 - sunspots

0
你可以使用Lambda表达式:
g = lambda l: [l[i:i+30] for i in range(0,50000, 30)]
gamma, delta = g(alpha), g(beta)

3
如果你给一个 lambda 命名,那它就没有成为 lambda 的必要了。 - cs95
2
请不要使用 lambda 来定义命名函数。lambda 应该用于匿名函数。如果您想要一个命名函数,请使用 def 语法。然而,我同意使用函数来完成这个任务可能是一个好主意。 - PM 2Ring
我同意评论中的观点,我也认为在这种情况下使用函数是一个更好的想法。有时候很难说哪种方法更符合Python风格。但是这似乎是函数的合法用例。 - SRC
我在很多地方看到这是定义单行函数的合法方式...为什么这是错误的? - Noa
1
所有函数都有一个__name__属性。这在调试等情况下非常方便(因此,如果您打印一个函数或将其转换为字符串,则名称属性是该字符串表示的一部分)。但是lambda函数的名称设置为'<lambda>',这并不是很有信息性。;) - PM 2Ring
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接