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
相关产品推荐
相关产品推荐

