在 Python 中设置理解并测试正在创建的集合中的成员资格

Set comprehensions in Python and testing for membership in the set being created

这其实是关于集合推导语义的问题,但我首先需要解释一下上下文。我正在尝试创建一组新的元组,其中无论对中值的顺序如何,元组中的配对值都是唯一的。简化我的实际程序,我有类似 {(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}

然而,newSet{(1, 2), (2, 1), (3, 4)},这意味着我的条件表达式有问题。我对理解的理解表明,上面的内容是这样的语法糖:

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

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

谢谢!

一个可行的替代方案是:

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

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

你误会了;集合理解是一个 不同的表达式 ,与赋值分开。该表达式生成一个 new set() 对象,然后将其分配给 newSetreplacing 旧的 set() 你拥有的对象。

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

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 来跟踪已经处理的内容,并在两个条件都为真时包含一个元组:

  • 倒数以前没见过,
  • 元组 returns 的 seen.add() 操作为假值。因为 seen.add() 总是 returns 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}