Python: 访问列表,因为它被理解
Python: Accessing list as it is comprehended
有没有办法访问所理解的列表?特别是我想再次迭代已经添加的元素。
例如,我正在寻找这样的东西:
[x for x in range(foo) if x not in self]
或
[x for x in range(foo) if any(y for y in self)]
其中 self 是刚刚理解的列表本身。
首先,any(y for y in self)
就是 any(self)
。
其次,您的函数不会执行任何操作。你有效地写的是:
res = []
for x in range(foo):
if any(res):
res.append(x)
这只是一个空列表。
你想要的是 set
.
res = set(values)
如果你关心顺序,你可以这样做:
seen = set()
[x for x in values if x not in seen and not seen.add(x)]
简而言之:不,你不能这样做。 Python 的列表推导(以及生成器表达式等相关构造)不如 Haskell 的惰性列表(可以做你想做的)强大。语法相似,但 Python 不是纯粹的函数式语言,具有惰性、递归求值作为语法特征;它的解释器不够复杂,无法理解和处理这样的行为。
更复杂的答案:在特定情况下,您可以通过滥用列表推导来绕过它(例如,acushner 的答案中使用的 set
更新黑客)。但是列表理解通常应该努力成为现有数据的转换和过滤器;他们不应该通过副作用来执行其他工作,因为这会使代码变得更加复杂,使维护者感到困惑。列表理解是一种功能模式;副作用显然是无功能的,所以人们不会寻找或期望它们。如果你需要做这样的事情,最好编写一个函数(可能是一个生成器函数)来隔离复杂性并维护跟踪到目前为止产生的值的内部状态。 itertools
recipes 中提供的各种独特配方就是一个很好的例子。
相当高级:Haskell 可以做到这一点,因为它的列表理解可以是惰性的,但可以重用(它有内存)。所以Haskell可以吃蛋糕也可以吃;列表可以根据先前的元素来定义元素,因为这些元素将在需要时生成,并且可以多次读取。 Python 将懒惰与可重用性区分开来。急切地评估列表理解;它在实际绑定到您可以用来访问它的名称之前完成填充(因此它无法在构建时从中间列表中读取值)。生成器表达式大多是惰性的(它急切地绑定它正在处理的可迭代对象,但其他一切都是惰性的)所以在它被评估时,它可以绑定到一个名称,但它没有记忆;您不能在 genexpr 内部使用 genexpr 中的值,因为这样做必然会推进生成器(在我能想到的大多数情况下,会导致无限递归,因为它试图根据下一个结果重复定义下一个结果,一遍又一遍)。
有没有办法访问所理解的列表?特别是我想再次迭代已经添加的元素。
例如,我正在寻找这样的东西:
[x for x in range(foo) if x not in self]
或
[x for x in range(foo) if any(y for y in self)]
其中 self 是刚刚理解的列表本身。
首先,any(y for y in self)
就是 any(self)
。
其次,您的函数不会执行任何操作。你有效地写的是:
res = []
for x in range(foo):
if any(res):
res.append(x)
这只是一个空列表。
你想要的是 set
.
res = set(values)
如果你关心顺序,你可以这样做:
seen = set()
[x for x in values if x not in seen and not seen.add(x)]
简而言之:不,你不能这样做。 Python 的列表推导(以及生成器表达式等相关构造)不如 Haskell 的惰性列表(可以做你想做的)强大。语法相似,但 Python 不是纯粹的函数式语言,具有惰性、递归求值作为语法特征;它的解释器不够复杂,无法理解和处理这样的行为。
更复杂的答案:在特定情况下,您可以通过滥用列表推导来绕过它(例如,acushner 的答案中使用的 set
更新黑客)。但是列表理解通常应该努力成为现有数据的转换和过滤器;他们不应该通过副作用来执行其他工作,因为这会使代码变得更加复杂,使维护者感到困惑。列表理解是一种功能模式;副作用显然是无功能的,所以人们不会寻找或期望它们。如果你需要做这样的事情,最好编写一个函数(可能是一个生成器函数)来隔离复杂性并维护跟踪到目前为止产生的值的内部状态。 itertools
recipes 中提供的各种独特配方就是一个很好的例子。
相当高级:Haskell 可以做到这一点,因为它的列表理解可以是惰性的,但可以重用(它有内存)。所以Haskell可以吃蛋糕也可以吃;列表可以根据先前的元素来定义元素,因为这些元素将在需要时生成,并且可以多次读取。 Python 将懒惰与可重用性区分开来。急切地评估列表理解;它在实际绑定到您可以用来访问它的名称之前完成填充(因此它无法在构建时从中间列表中读取值)。生成器表达式大多是惰性的(它急切地绑定它正在处理的可迭代对象,但其他一切都是惰性的)所以在它被评估时,它可以绑定到一个名称,但它没有记忆;您不能在 genexpr 内部使用 genexpr 中的值,因为这样做必然会推进生成器(在我能想到的大多数情况下,会导致无限递归,因为它试图根据下一个结果重复定义下一个结果,一遍又一遍)。