You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

向集合添加相同元组却生成元组排列项的问题排查

问题描述

尝试生成由5个二元整数元组组成的元组的唯一排列,通过set.add()方法去重,但执行代码时发现:即使打印出要添加的列表与首次完全相同,集合中仍会出现元素重排的新项;循环8次后集合中出现8个不同排列项,而预期应仅保留1个唯一项。

代码如下:

def generate_each_orientation(self):
    self.orientations = set()
    working = self.raw[:]
    Piece.normalize(working)
    for i in range(8):
        print("Iteration ",i+1)
        print("List to be added to set: ", working)
        self.orientations.add(tuple(working))
        print("Set after adding list:\n",self.orientations)
        [Tile.rotate(t) for t in working]
        Piece.normalize(working)
        if i == 3:
            [Tile.flip(t) for t in working]
            Piece.normalize(working)

执行输出示例:

第一次迭代:
Iteration 1
List to be added to set: [(0, 0), (1, 0), (1, -1), (1, 1), (2, 0)]
Set after adding list:
{((0, 0), (1, 0), (1, -1), (1, 1), (2, 0))}

第二次迭代:
Iteration 2
List to be added to set: [(0, 0), (1, 0), (1, -1), (1, 1), (2, 0)]
Set after adding list:
{((1, -1), (1, 0), (2, 0), (0, 0), (1, 1)), ((0, 0), (1, 0), (1, -1), (1, 1), (2, 0))}

问题原因分析
  1. 元素类型误解:你打印working时看到的是Tile对象的坐标字符串(由__repr__方法输出),但working实际存储的是Tile类的实例对象,而非二元整数元组。执行tuple(working)生成的是包含Tile对象引用的元组,而非坐标元组的元组。
  2. 可变对象的副作用:Tile.rotate(t)和Tile.flip(t)是原地修改Tile对象的内部坐标属性,而集合中存储的元组引用了这些可变对象。当后续修改Tile对象状态时,集合中已存在的元组的显示内容会跟着变化(因为__repr__输出的是当前对象状态),导致你误以为集合中新增了“重排项”,但实际是原有元组的元素状态被修改了。
  3. 集合去重逻辑失效:set依赖元素的不可变性和正确的__hash__/__eq__实现判断唯一性。如果Tile类未基于坐标实现__hash__和__eq__,默认会以对象内存地址作为判断依据——即使两个Tile对象坐标相同,也会被视为不同元素;同时,可变对象状态变化后,集合的哈希表会出现不一致,无法正确识别重复项。
解决办法

方案一(推荐):添加不可变的坐标元组到集合

直接提取Tile对象的坐标(二元整数元组),生成不可变的元组添加到集合,彻底避免可变对象的副作用。修改添加集合的代码:

# 假设Tile对象通过coords属性存储(x, y)二元元组
self.orientations.add(tuple(t.coords for t in working))

# 若Tile类实现了__iter__方法可直接解包坐标,也可使用:
# self.orientations.add(tuple(tuple(t) for t in working))

这种方式下,集合存储的是纯整数元组,哈希值稳定,能准确判断唯一性,不受Tile对象状态变化或列表顺序调整的影响。

方案二:修正Tile类的哈希与相等性实现

若必须存储Tile对象的元组,需在Tile类中基于坐标实现__hash__和__eq__,确保坐标相同的Tile被视为同一元素:

class Tile:
    def __init__(self, x, y):
        self.coords = (x, y)  # 用不可变元组存储坐标
    
    def rotate(self):
        # 原地修改坐标示例,确保修改后更新coords
        x, y = self.coords
        self.coords = (-y, x)
    
    def flip(self):
        # 原地翻转坐标示例
        x, y = self.coords
        self.coords = (-x, y)
    
    def __hash__(self):
        # 基于坐标生成哈希值
        return hash(self.coords)
    
    def __eq__(self, other):
        # 基于坐标判断相等
        if not isinstance(other, Tile):
            return False
        return self.coords == other.coords
    
    def __repr__(self):
        # 输出坐标字符串,保持打印一致性
        return str(self.coords)

注意:此方案仍需确保Tile对象的状态修改不会破坏哈希的稳定性,相比方案一,不可变性更弱,潜在问题更多。

内容的提问来源于stack exchange,提问作者user206545

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 15:07:08