2

这实际上是关于集合推导的语义的问题,但我首先需要解释一下上下文。我正在尝试创建一组新的元组,其中 touple 中的配对值是唯一的,无论配对中值的顺序如何。简化我的实际程序,我所拥有的是{(1, 2), (2, 1), (3, 4)}我想得到的东西{(1, 2), (3, 4)}

我试着做这样的事情:

oldSet = {(1, 2), (2, 1), (3, 4)}

newSet = set()
newSet = {(val1, val2) for (val1, val2) in oldSet if not (val2, val1) in newSet}

但是,newSetis{(1, 2), (2, 1), (3, 4)}暗示我的条件表达式有问题。我对理解的理解表明,上面是这样的语法糖:

newSet =  set()
for (val1, val2) in oldSet:
  if not (val2, val1) in newSet:
    newSet.add((val1, val2))

这种传统的循环结构有效(newSetis {(1, 2), (3, 4)})。是否有一些关于理解的东西导致条件在newSet有任何成员之前被评估?我对 Python 还很陌生,所以我想知道我是否遗漏了一些微妙的东西。

谢谢!

4

2 回答 2

5

你误会了; 集合推导是一个独特的表达式,与赋值分开。该表达式生成一个 set()对象,然后将其分配给newSet替换您拥有的旧set()对象。

因此,当您迭代和构建集合时,set()绑定到的先前和单独的对象newSet 保持为空。实际上,集合推导是这样做的:

newSet = set()
_result = set()
for (val1, val2) in oldSet:
    if not (val2, val1) in newSet:
        result.add((val1, val2))
newSet = _result

您可以在迭代时使用副作用来更改单独的集合:

seen = set()
newSet = {(val1, val2) for (val1, val2) in oldSet
          if not ((val2, val1) in seen or seen.add((val1, val2))}

这用于seen跟踪已经处理的内容,如果两个条件都为真,则包含一个元组:

  • 以前从未见过倒数,
  • 元组的seen.add()操作返回一个假值。因为seen.add()总是返回None,所以总是这样。

请注意,这现在两次构建相同的集合,因此您最好执行常规循环并完成它:

newSet = set()
for (val1, val2) in oldSet:
    if not (val2, val1) in newSet:
        newSet.add((val1, val2))

由于您的元组仅包含两个值,因此您不妨在这里使用排序;(a, b), (b, a)毕竟,任何一对元组都有一个唯一的排序:

newSet = {tuple(sorted(t)) for t in oldSet}
于 2015-06-19T15:49:01.610 回答
2

一个可行的替代方案是:

newSet = { tuple(sorted(t)) for t in oldSet }

您的解决方案是检查正在生成的集合中是否存在元组,但名称尚未绑定到值。它会在理解终止时。

于 2015-06-19T15:47:13.597 回答