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

Python随机游走dirtyField子类randint报错及逻辑问题求助

问题排查与修正:DirtyField子类的KeyboardInterrupt及逻辑错误

问题描述

基于Location、Drunk、Field类实现了DirtyField子类,该子类会在区域内生成脏网格,要求随机游走者踩中脏网格时持续移动。但实例化DirtyField时触发了randint相关的KeyboardInterrupt,同时代码逻辑存在问题,需要排查并修正。

错误栈信息

KeyboardInterrupt                         Traceback (most recent call last)
Input In [20], in <cell line: 27>()
     22         self.party[motive] =\
     23                 self.party[motive].move(x, y)
     26 start = Location(0, 0)
---> 27 f = dirtyField()
     29 homer = SouthDrunk('Homer')
     30 f.addDrunk(homer, start)

Input In [20], in dirtyField.__init__(self, dirtyTiles, xRange, yRange)
      6 w = 0
      7 while (w < dirtyTiles):
----> 8     x = random.randint(-xRange, xRange)
      9     y = random.randint(-yRange, yRange)
     10     aDirtyTile = Location(x, y)

File ~\anaconda3\lib\random.py:338, in Random.randint(self, a, b)
    334 def randint(self, a, b):
    335     """Return random integer in range [a, b], including both end points.
    336     """
---> 338     return self.randrange(a, b+1)

File ~\anaconda3\lib\random.py:314, in Random.randrange(self, start, stop, step)
    312 width = istop - istart
    313 if step == 1 and width > 0:
---> 314     return istart + self._randbelow(width)
    315 if step == 1:
    316     raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width))

File ~\anaconda3\lib\random.py:243, in Random._randbelow_with_getrandbits(self, n)
    241     return 0
    242 getrandbits = self.getrandbits
---> 243 k = n.bit_length()  # don't use (n-1) here because n can be 1
    244 r = getrandbits(k)  # 0 <= r < 2**k
    245 while r >= n:

KeyboardInterrupt:

原实现代码

class dirtyField(Field):
    def __init__(self, dirtyTiles = 1000,
                 xRange = 100, yRange = 100):
        Field.__init__(self)
        self.dirtTile = []
        w = 0
        while (w < dirtyTiles):
            x = random.randint(-xRange, xRange)
            y = random.randint(-yRange, yRange)
            aDirtyTile = Location(x, y)
            self.dirtTile.append(aDirtyTile)
            
    def moveDrunk(self, motive):
        # per instructions if the axis is a dirty tile then the drunk moves until a clean tile.
        # one tile at a time motive is another
        Field.moveDrunk(self, motive)
        while (self.party[motive] in self.dirtTiles):
            self.party[motive] =\
                self.party[motive].move(x, y)
            x, y = motive.takeStep()
            
        self.party[motive] =\
                self.party[motive].move(x, y)

start = Location(0, 0)
f = dirtyField()

homer = SouthDrunk('Homer')
f.addDrunk(homer, start)
f.moveDrunk(homer)
print(f.getLoc(homer))

错误排查与修正

1. KeyboardInterrupt问题

这个中断是手动终止程序(比如按下Ctrl+C)导致的,但原初始化代码存在效率缺陷:用while循环+列表存储脏网格会重复添加大量相同的Location对象,导致循环耗时过长,这是你忍不住中断的诱因。

修正方案:改用集合存储脏网格,自动去重,直到集合大小达到指定数量:

def __init__(self, dirty_tiles=1000, x_range=100, y_range=100):
    super().__init__()  # 用super()更规范,支持多继承
    self.dirt_tiles = set()  # 集合自动去重,避免重复生成相同网格
    while len(self.dirt_tiles) < dirty_tiles:
        x = random.randint(-x_range, x_range)
        y = random.randint(-y_range, y_range)
        self.dirt_tiles.add(Location(x, y))

注意:需确保Location类实现__hash__和__eq__方法,否则集合无法正确去重,补充代码如下:

class Location:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __hash__(self):
        return hash((self.x, self.y))
    
    def __eq__(self, other):
        return isinstance(other, Location) and self.x == other.x and self.y == other.y

2. moveDrunk方法的逻辑错误

  • 变量名不一致:初始化时用self.dirtTile,判断时写成self.dirtTiles,导致判断失效
  • 未初始化变量:进入while循环前未调用takeStep()获取移动方向,会触发NameError
  • 重复移动:while循环结束后额外执行一次move,导致多走一步
  • 逻辑顺序错误:先调用父类moveDrunk再判断,会导致初始移动后才检查是否在脏网格,不符合需求

修正后的moveDrunk方法:

def moveDrunk(self, motive):
    current_loc = self.party[motive]
    # 踩中脏网格则持续移动直到离开
    while current_loc in self.dirt_tiles:
        dx, dy = motive.takeStep()
        current_loc = current_loc.move(dx, dy)
        self.party[motive] = current_loc
    # 不在脏网格则正常移动一次
    dx, dy = motive.takeStep()
    self.party[motive] = current_loc.move(dx, dy)

3. 其他细节修正

  • 类名遵循PEP8规范:dirtyField改为DirtyField
  • 变量名统一:dirtTile改为dirt_tiles(复数更贴合语义)
  • 父类构造调用:用super().__init__()替代Field.__init__(self),更符合Python面向对象规范

完整修正代码

import random

# 补充Location类实现(若已有可忽略)
class Location:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def move(self, dx, dy):
        return Location(self.x + dx, self.y + dy)
    
    def __hash__(self):
        return hash((self.x, self.y))
    
    def __eq__(self, other):
        return isinstance(other, Location) and self.x == other.x and self.y == other.y
    
    def __repr__(self):
        return f"Location({self.x}, {self.y})"

# 补充Drunk及子类实现(若已有可忽略)
class Drunk:
    def __init__(self, name):
        self.name = name
    
    def takeStep(self):
        choices = [(0,1), (0,-1), (1,0), (-1,0)]
        return random.choice(choices)

class SouthDrunk(Drunk):
    def takeStep(self):
        return (0, -1)

# 补充Field类实现(若已有可忽略)
class Field:
    def __init__(self):
        self.party = {}
    
    def addDrunk(self, drunk, loc):
        if drunk not in self.party:
            self.party[drunk] = loc
    
    def getLoc(self, drunk):
        return self.party.get(drunk)
    
    def moveDrunk(self, drunk):
        x, y = drunk.takeStep()
        self.party[drunk] = self.party[drunk].move(x, y)

# 修正后的DirtyField类
class DirtyField(Field):
    def __init__(self, dirty_tiles=1000, x_range=100, y_range=100):
        super().__init__()
        self.dirt_tiles = set()
        while len(self.dirt_tiles) < dirty_tiles:
            x = random.randint(-x_range, x_range)
            y = random.randint(-y_range, y_range)
            self.dirt_tiles.add(Location(x, y))
    
    def moveDrunk(self, motive):
        current_loc = self.party[motive]
        # 踩中脏网格持续移动
        while current_loc in self.dirt_tiles:
            dx, dy = motive.takeStep()
            current_loc = current_loc.move(dx, dy)
            self.party[motive] = current_loc
        # 正常移动一次
        dx, dy = motive.takeStep()
        self.party[motive] = current_loc.move(dx, dy)

# 测试代码
start = Location(0, 0)
f = DirtyField()

homer = SouthDrunk('Homer')
f.addDrunk(homer, start)
f.moveDrunk(homer)
print(f.getLoc(homer))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 20:55:21