我有时会看到这样的事情:
(k for k in (j for j in (i for i in xrange(10))))
现在这真的让我的大脑弯曲,我宁愿它不以这种方式呈现。
有没有使用这些嵌套表达式的用例或示例,它比嵌套循环更优雅、更易读?
编辑:感谢您提供简化此方法的示例。这实际上不是我要求的,我想知道是否有任何时候它很优雅。
我有时会看到这样的事情:
(k for k in (j for j in (i for i in xrange(10))))
现在这真的让我的大脑弯曲,我宁愿它不以这种方式呈现。
有没有使用这些嵌套表达式的用例或示例,它比嵌套循环更优雅、更易读?
编辑:感谢您提供简化此方法的示例。这实际上不是我要求的,我想知道是否有任何时候它很优雅。
检查PEP 202,这是将列表推导语法引入该语言的地方。
为了理解你的例子,Guido 自己有一个简单的规则:
同样来自 PEP 202,它可以回答您的问题:
基本原理 列表推导式提供了一种更简洁的方式来创建列表 map() 和 filter() 和/或嵌套循环的情况 目前正在使用。
如果你有这样的情况,你会发现它更优雅。但是,恕我直言,在您的代码中,多个嵌套列表推导式可能不如嵌套 for 循环清晰,因为for
循环很容易在视觉上解析。
如果您担心一行太复杂,您可以拆分它:
(k for k in
(j for j in
(i for i in xrange(10))))
我总是发现在 Python 中的续行看起来有点奇怪,但这确实可以更容易地看到每个续行在循环的内容。由于额外的分配/查找不会造成或破坏任何东西,你也可以这样写:
gen1 = (i for i in xrange(10))
gen2 = (j for j in gen1)
gen3 = (k for k in gen2)
在实践中,我认为我从来没有嵌套过超过 2 层的理解,在那时它仍然很容易理解。
在您的示例中,我可能会将其写为:
foos = (i for i in xrange(10))
bars = (j for j in foos)
bazs = (k for k in bars)
鉴于更具描述性的名称,我认为这可能会很清楚,而且我无法想象会有任何可衡量的性能差异。
也许您正在考虑更多的表达方式,例如:
(x for x in xs for xs in ys for ys in lst)
——实际上,这甚至是无效的。你必须把东西放在另一个顺序:
(x for ys in lst for xs in ys for x in xs)
我可能会把它写成扁平化列表的一种快速方法,但总的来说,我认为您是这样写的:通过键入更少的内容节省的时间通常与您花费在正确生成生成器表达式上的额外时间相平衡。
由于它们是生成器表达式,因此您可以将每个表达式绑定到它自己的名称,以使其更具可读性,而不会改变性能。将其更改为嵌套循环可能会损害性能。
irange = (i for i in xrange(10))
jrange = (j for j in irange)
krange = (k for k in jrange)
选择哪个并不重要,我认为多行示例通常更具可读性。
警告:优雅部分是品味问题。
列表推导永远不会比相应的扩展 for 循环更清晰。For 循环也比列表推导更强大。那么为什么要使用它们呢?
列表推导是简洁的——它们允许你在一行中做一些事情。
使用列表推导式的时机是当您需要某个列表时,它可以相当容易地动态创建,并且您不希望或不需要中间对象。当您需要将当前范围内的一些对象打包成一个可以输入函数的对象时,可能会发生这种情况,如下所示:
list1 = ['foo', 'bar']
list2 = ['-ness', '-ity']
return filterRealWords([str1+str2 for str1 in list1 for str2 in list2])
这段代码的可读性与扩展版本差不多,但要短得多。它避免了创建/命名在当前范围内仅使用一次的对象,这可以说是更优雅。
表达方式:
(k for k in (j for j in (i for i in xrange(10))))
相当于:
(i for i in xrange(10))
这几乎是一样的:
xrange(10)
最后一个变体比第一个变体更优雅。
我发现它在你有这样的代码的情况下是有用和优雅的:
output = []
for item in list:
if item >= 1:
new = item += 1
output.append(new)
你可以把它做成这样的单行:
output = [item += 1 for item in list if item >= 1]