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

Python自定义State类获取二维数组返回实例触发下标报错

8数码启发式搜索程序下标报错问题分析

问题表现

自定义State类封装二维列表格式的网格状态,类内实现getState()方法返回内部存储的二维数组。预期调用该方法拿到数组后可正常执行下标访问、元素交换等列表操作,但部分场景下获取到的值类型为<class '__main__.State'>而非预期的list,运行时抛出TypeError: 'State' object is not subscriptable错误。
经实测:

  • 仅find_h()函数内部调用state.getState()时可正常返回list类型
  • 输入空白格坐标i=2、j=0运行代码时,报错触发点为generateChildren()函数上移逻辑的网格元素交换步骤

报错栈信息如下:

Traceback (most recent call last):
line 122, in
driver()
line 115, in driver
generateChildren(current_state)
in generateChildren
grid[i-1][j], grid[i][j] = grid[i][j], grid[i-1][j]
TypeError: 'State' object is not subscriptable

根因分析

代码存在5处逻辑错误,共同导致报错及后续逻辑异常:

  1. State实例化传参类型错误:generateChildren()的上移、左移、右移分支中,创建新节点的代码为new_state = State(state, 0, state),传入的第一个参数state本身是State类实例,而非二维列表,导致新节点的self.state属性存储的是State对象,后续访问该属性尝试做列表下标操作时必然触发类型错误。
  2. 当前节点二次包装错误:driver()函数主循环中写了current_state = State(queue.pop(0)),而queue中存储的元素本身就是初始化完成的State实例,将该实例作为参数再次传入State构造函数,直接导致传入generateChildren()的current_state内部嵌套了一层State对象,是本次报错的直接触发点。
  3. 未做状态深拷贝:所有移动逻辑直接在原父节点的grid引用上修改元素,会直接篡改父节点、队列中其他节点、已访问列表中节点的状态,导致搜索过程中状态数据完全混乱。
  4. 右移逻辑边界判断错误:右移分支的判断条件误写为0 <= i-1 < 3 and 0 <= j < 3,未判断j+1的坐标合法性,导致右移逻辑永远无法正常触发。
  5. 空白格坐标使用全局固定值:generateChildren()中直接使用全局输入的blank_i、blank_j作为当前节点的空白格坐标,没有根据当前处理的网格动态计算0的位置,节点移动1次后空白格坐标就会和实际值不匹配。

修复方案

针对上述错误逐点修正即可:

  • 所有创建State实例的位置,第一个参数必须传入二维列表类型的网格数据,禁止直接传入State对象
  • 移除driver()中对弹出队列节点的二次State包装,直接将弹出的State实例赋值给current_state
  • 生成子节点时,先对父节点的二维网格做深拷贝,在拷贝出的独立网格上执行元素交换,避免修改父节点状态
  • 修正右移分支的边界判断逻辑,判断j+1是否在0-2的合法范围内
  • 废弃全局固定的空白格坐标,每次处理当前节点时,先遍历网格找到值为0的位置作为当前空白格坐标,再执行移动逻辑

修正后的核心代码片段如下:

import copy

def generateChildren(state):
    # 动态计算当前状态的空白格坐标
    grid = state.getState()
    i, j = 0, 0
    for x in range(3):
        for y in range(3):
            if grid[x][y] == 0:
                i, j = x, y
                break
    # 上移
    if 0 <= i-1 < 3:
        new_grid = copy.deepcopy(grid)
        new_grid[i-1][j], new_grid[i][j] = new_grid[i][j], new_grid[i-1][j]
        new_state = State(new_grid, 0, state)
        new_state.h = find_h(new_state)
        queue.append(new_state)
    # 下移
    if 0 <= i+1 < 3:
        new_grid = copy.deepcopy(grid)
        new_grid[i+1][j], new_grid[i][j] = new_grid[i][j], new_grid[i+1][j]
        new_state = State(new_grid, 0, state)
        new_state.h = find_h(new_state)
        queue.append(new_state)
    # 左移
    if 0 <= j-1 < 3:
        new_grid = copy.deepcopy(grid)
        new_grid[i][j-1], new_grid[i][j] = new_grid[i][j], new_grid[i][j-1]
        new_state = State(new_grid, 0, state)
        new_state.h = find_h(new_state)
        queue.append(new_state)
    # 右移
    if 0 <= j+1 < 3:
        new_grid = copy.deepcopy(grid)
        new_grid[i][j+1], new_grid[i][j] = new_grid[i][j], new_grid[i][j+1]
        new_state = State(new_grid, 0, state)
        new_state.h = find_h(new_state)
        queue.append(new_state)

def driver():
    goal = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]
    start_grid = [[3, 7, 6], [8, 4, 2], [0, 1, 5]]
    start = State(start_grid)
    h = find_h(start)
    start.setH(h)
    global sol
    queue.append(start)
    while queue:
        queue.sort(key=lambda x: x.h)
        # 移除多余的State二次包装
        current_state = queue.pop(0)
        if goalstate(current_state, goal):
            sol += 1
            traceback(current_state)
            break
        else:
            if inVisited(current_state):
                continue
            visited.append(current_state)
            generateChildren(current_state)

注:原代码visited列表只做了查询没有写入节点,修复时补充了访问后将节点加入visited的逻辑,避免重复搜索死循环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 00:03:20