列表理解中函数的 Pythonic 改进?

标签 python list-comprehension

是否有更 pythonic 的方法来执行以下代码?我想在一行中完成

parsed_rows 是一个可以返回大小为 3 或 None 的元组的函数。

parsed_rows = [ parse_row(tr) for tr in tr_els ]        
data        = [ x for x in parsed_rows if x is not None ] 

最佳答案

在一行中执行此操作不会使它更像 Pythonic;它会使它的可读性降低。如果你真的想要,你总是可以通过这样的替换直接翻译它:

data = [x for x in [parse_row(tr) for tr in tr_els] if x is not None] 

…这显然可以像《雪之门 Handlebars 》中显示的那样被压扁,但仍然难以理解。然而,他没有完全理解:子句从左到右嵌套,你希望 x 是每个 parse_row 结果,而不是每个 的每个元素parse_row 结果(如 Volatility 指出的那样),因此扁平化版本将是:

data = [x for tr in tr_els for x in (parse_row(tr),) if x is not None]

我认为,一个优秀的开发人员在任何人意识到问题之前将其倒退并有 6 人投票支持,然后我错过了第二个问题并且在任何人发现它之前又有 7 人投票支持,这是非常可靠的证据不像 Doorknob 所说的那样更加 pythonic 或更具可读性。 :)

一般来说,当面对一个嵌套的 comp 或一个带有多个 for 子句的 comp 时,如果它的作用不是很明显,你应该将它翻译成嵌套的 for > 和 if 语句与最内层的 append 表达式语句,如 the tutorial 所示.但是,如果您需要通过您正在尝试编写的理解来做到这一点,那么这是一个很好的迹象,您不应该尝试编写它......


但是, 有一种方法可以使它更像 Pythonic,也更高效:将第一个列表理解更改为生成器表达式,如下所示:

parsed_rows = (parse_row(tr) for tr in tr_els)
data = [x for x in parsed_rows if x is not None]

我所做的只是将方括号更改为圆括号,这足以延迟计算第一个,在每个 tr 上调用 parse_row根据需要,而不是在所有行上调用它,并在您开始实际工作之前在内存中构建一个您实际上不需要的列表。

事实上,如果您需要 data 的唯一原因是对其进行一次迭代(或将其转换为其他形式,如 CSV 文件或 NumPy 数组),您可以这样做还有一个生成器表达式。


或者,更好的是,将列表理解替换为 map称呼。当您的表达式只是“在每个元素上调用此函数”时,map 通常更具可读性(而当您必须编写一个新函数,尤其是使用 lambda 时,只是为了包装一些更复杂的表达式,通常不是)。所以:

parsed_rows = map(parse_row, tr_els)
data = [x for x in parsed_rows if x is not None]

现在它实际上可读的:

data = [x for x in map(parse_row, tr_els) if x is not None]

您可以类似地将第二个理解转换为 filter称呼。然而,就像 map 一样,如果谓词不仅仅是“调用这个函数并查看它是否返回真实的东西”,它通常最终会变得可读性降低。在这种情况下:

data = filter(lambda x: x is not None, map(parse_row, tr_els))

但是请注意,您真的不需要首先检查 is not None。您拥有的唯一非 None 值是 3 元组,它们始终为真。因此,您可以将 if x is not None 替换为 if x,这可以简化您的理解:

data = [x for x in map(parse_row, tr_else) if x]

... 并且可以使用 filter 以两种不同的方式编写:

data = filter(bool, map(parse_row, tr_els))
data = filter(None, map(parse_row, tr_els))

询问这两者中哪一个更好将在任何 Python 列表上引发一场宗教 war ,所以我只展示它们并让您决定。


请注意,如果您使用的是 Python 2.x,map 不是惰性的;它将生成整个中间列表。所以,如果您想两全其美,又不能使用 Python 3,请使用 itertools.imap 而不是 map。同样,在 3.x 中,filter 是惰性的,所以如果你想要一个列表,请使用 list(filter(…)).

关于列表理解中函数的 Pythonic 改进?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20692637/

相关文章:

python - 在 Matplotlib 中如何防止图像出现在屏幕上? Python

python - 删除Python中所有子进程的输出而无需访问代码

python - 如何更快地从列表中删除包含某些单词的字符串

列表列表,取下一个元素

python - 将列表推导用于副作用是 Pythonic 吗?

python - 如何在python中一次输入一个字母?

Python - 将 HDF5 数据集读入列表与 numpy 数组

python - 数据流中的值错误 : Invalid GCS location: None

f# - F# 中带有浮点迭代器的列表推导式

python——在列表上应用 lstrip 的递归列表理解