从游戏到实战:用ICode综合练习6的代码,教你写出更优雅的Python循环
在编程学习的过程中,我们常常会遇到这样的困境:理解了基础语法,却不知道如何写出简洁高效的代码;掌握了理论知识,却难以在实际项目中灵活运用。ICode国际青少年编程竞赛的训练场题目,恰恰为我们提供了一个绝佳的实践平台。这些看似简单的游戏化题目,实则蕴含着Python编程的核心思想和最佳实践。
本文将带你深入分析ICode综合练习6中的代码示例,揭示其中可以优化的地方,并展示如何用更Pythonic的方式重构这些循环结构。无论你是希望夯实Python基础的编程爱好者,还是正在寻找教学案例的编程教师,这些从竞赛题目中提炼出的编码技巧,都能帮助你写出更优雅、更高效的Python代码。
1. 循环结构的基本优化原则
在开始具体案例分析之前,我们先来了解几个Python循环优化的基本原则。这些原则不仅适用于ICode题目,也是日常编程中的通用准则。
1.1 避免重复计算
观察原始代码中的第一个例子:
for i in range(3): Dev.step(2 * (i + 1)) Dev.turnLeft() while Flyer[2 - i].disappear(): wait() Dev.step(2 * (i + 1)) Dev.turnRight() while Dev.x != Item[i].x: wait()这里2 * (i + 1)被计算了两次。我们可以将其计算结果存储在变量中:
for i in range(3): step_size = 2 * (i + 1) Dev.step(step_size) Dev.turnLeft() while Flyer[2 - i].disappear(): wait() Dev.step(step_size) Dev.turnRight() while Dev.x != Item[i].x: wait()1.2 使用更合适的循环结构
原始代码中大量使用了while循环等待条件满足,这种模式可以抽象为辅助函数:
def wait_until(condition): while not condition(): wait() for i in range(3): step_size = 2 * (i + 1) Dev.step(step_size) Dev.turnLeft() wait_until(lambda: Flyer[2 - i].disappear()) Dev.step(step_size) Dev.turnRight() wait_until(lambda: Dev.x == Item[i].x)2. 列表推导式与生成器表达式的应用
Python的列表推导式和生成器表达式是简化循环的利器。让我们看第二个例子的优化过程。
原始代码:
for i in range(3): Dev.step(2 * i + 1) while Item[2 * i].y != Dev.y: wait() for j in range(2): Dev.step(2) Dev.turnRight() Dev.step(2 * (i + 1)) Dev.turnLeft() while Flyer[i].disappear(): wait() Dev.step(2) Dev.turnLeft() while Item[2 * i + 1].x != Dev.x or Item[2 * i + 1].y != Dev.y: wait()我们可以将重复的模式提取出来,并使用更简洁的表达方式:
def move_and_wait(steps, turn=None, wait_condition=None): Dev.step(steps) if turn: getattr(Dev, turn)() if wait_condition: wait_until(wait_condition) for i in range(3): actions = [ (2*i+1, None, lambda: Item[2*i].y == Dev.y), (2, 'turnRight', None), (2, 'turnRight', None), (2*(i+1), 'turnLeft', lambda: Flyer[i].disappear()), (2, 'turnLeft', lambda: Item[2*i+1].x == Dev.x and Item[2*i+1].y == Dev.y) ] for steps, turn, condition in actions: move_and_wait(steps, turn, condition)3. 函数式编程思维的运用
函数式编程可以让我们写出更声明式、更易理解的代码。让我们看看第三个例子的优化。
原始代码:
for i in range(7): Dev.step(1) Dev.turnLeft() Dev.step(8 - Item[i].x) Dev.step(Item[i].x - 9) while Dev.energy < 100: wait() Dev.step(1) Dev.turnRight() Dev.step(1)我们可以将操作序列抽象为函数组合:
def compose(*functions): return lambda x: reduce(lambda v, f: f(v), functions, x) def step(n): return lambda dev: dev.step(n) def turn(direction): return lambda dev: getattr(dev, f'turn{direction}')() def wait_for_energy(min_energy=100): return lambda dev: wait_until(lambda: dev.energy >= min_energy) actions = [ step(1), turn('Left'), lambda dev: dev.step(8 - Item[i].x), lambda dev: dev.step(Item[i].x - 9), wait_for_energy(), step(1), turn('Right'), step(1) ] for i in range(7): compose(*actions)(Dev)4. 面向对象的重构思路
对于更复杂的逻辑,我们可以考虑使用面向对象的方式组织代码。以第四个例子为例:
原始代码:
for i in range(4): while Flyer[i].disappear(): wait() Dev.step(4 + i) while Item[2 * i].y != Dev.y: wait() if i < 3: Dev.step(-2 - i) while Flyer[i].disappear(): wait() Dev.step(-2) Dev.turnRight() Dev.step(2 * i + 2) Dev.turnLeft()我们可以创建一个机器人控制器类:
class RobotController: def __init__(self, robot): self.robot = robot def move(self, steps): self.robot.step(steps) return self def turn(self, direction): getattr(self.robot, f'turn{direction}')() return self def wait_for(self, condition): wait_until(condition) return self controller = RobotController(Dev) for i in range(4): (controller.wait_for(lambda: Flyer[i].disappear()) .move(4 + i) .wait_for(lambda: Item[2*i].y == Dev.y)) if i < 3: (controller.move(-2 - i) .wait_for(lambda: Flyer[i].disappear()) .move(-2) .turn('Right') .move(2*i + 2) .turn('Left'))5. 上下文管理器与资源管理
在处理需要等待资源或状态的场景时,Python的上下文管理器非常有用。让我们优化第五个例子:
原始代码:
for i in range(5): Dev.step(3 + i) while Dev.energy < 100: wait() Dev.step(i + 1) Dev.turnLeft()我们可以创建一个能量管理的上下文管理器:
from contextlib import contextmanager @contextmanager def ensure_energy(device, min_energy=100): wait_until(lambda: device.energy >= min_energy) yield wait_until(lambda: device.energy >= min_energy) for i in range(5): Dev.step(3 + i) with ensure_energy(Dev): Dev.step(i + 1) Dev.turnLeft()6. 使用枚举替代魔术数字
原始代码中大量使用了魔术数字,这使得代码难以理解和维护。让我们优化第六个例子:
原始代码:
for i in range(4): while Flyer[i].disappear(): wait() Dev.step(4) while Flyer[4 + i].disappear(): wait() Dev.step(1) Dev.turnLeft() Dev.step(2) Dev.step(-1) while Flyer[4 + i].disappear(): wait() Dev.step(-1) Dev.turnLeft() Dev.step(3) Dev.turnRight() Dev.step(2) Dev.step(-2) Dev.turnLeft() while Flyer[i].disappear(): wait() Dev.step(2) Dev.turnLeft()我们可以使用枚举和常量来替代这些魔术数字:
BASE_STEP = 4 TURN_STEP = 2 ADJUST_STEP = 1 for i in range(4): wait_until(lambda: Flyer[i].disappear()) Dev.step(BASE_STEP) wait_until(lambda: Flyer[4 + i].disappear()) Dev.step(ADJUST_STEP) Dev.turnLeft() Dev.step(TURN_STEP) Dev.step(-ADJUST_STEP) wait_until(lambda: Flyer[4 + i].disappear()) Dev.step(-ADJUST_STEP) Dev.turnLeft() Dev.step(3) # 这个3可能需要定义为常量 Dev.turnRight() Dev.step(TURN_STEP) Dev.step(-TURN_STEP) Dev.turnLeft() wait_until(lambda: Flyer[i].disappear()) Dev.step(TURN_STEP) Dev.turnLeft()7. 策略模式处理复杂条件逻辑
对于包含复杂条件逻辑的循环,策略模式可以帮助我们更好地组织代码。以第七个例子为例:
原始代码:
for i in range(4): Dev.step(-2) while Flyer[i].disappear(): wait() Dev.step(-4 - i) while Item[i].x != Dev.x or Item[i].y != Dev.y: wait() Dev.step(i + 2) while Flyer[i].disappear(): wait() Dev.step(4) Dev.turnLeft()我们可以定义不同的移动策略:
class MoveStrategy: def execute(self, robot, index): pass class InitialMoveStrategy(MoveStrategy): def execute(self, robot, index): robot.step(-2) wait_until(lambda: Flyer[index].disappear()) robot.step(-4 - index) class ItemMoveStrategy(MoveStrategy): def execute(self, robot, index): wait_until(lambda: Item[index].x == robot.x and Item[index].y == robot.y) robot.step(index + 2) class FinalMoveStrategy(MoveStrategy): def execute(self, robot, index): wait_until(lambda: Flyer[index].disappear()) robot.step(4) robot.turnLeft() strategies = [InitialMoveStrategy(), ItemMoveStrategy(), FinalMoveStrategy()] for i in range(4): for strategy in strategies: strategy.execute(Dev, i)8. 使用装饰器简化重复逻辑
装饰器是Python中强大的工具,可以用来简化重复的逻辑。让我们优化第八个例子:
原始代码:
for i in range(4): Spaceship.step(1) while not Flyer[i].disappear(): wait() Spaceship.step(2) Dev.turnRight() Dev.step(3) Dev.step(-4) for j in range(4): Dev.step(-2) Dev.turnRight() Dev.step(1) Spaceship.step(4) Spaceship.turnRight()我们可以创建等待装饰器:
def wait_for_flyer_disappear(index): def decorator(func): def wrapper(*args, **kwargs): wait_until(lambda: Flyer[index].disappear()) return func(*args, **kwargs) return wrapper return decorator @wait_for_flyer_disappear(i) def spaceship_move(): Spaceship.step(1) Spaceship.step(2) @wait_for_flyer_disappear(i) def dev_move(): Dev.turnRight() Dev.step(3) Dev.step(-4) for _ in range(4): Dev.step(-2) Dev.turnRight() Dev.step(1) for i in range(4): spaceship_move() dev_move() Spaceship.step(4) Spaceship.turnRight()9. 利用Python的数据结构简化代码
Python丰富的数据结构可以帮助我们写出更简洁的代码。让我们优化第九个例子:
原始代码:
for i in range(5): Dev.step(Dev.y - Flyer[4 - i].y) Dev.turnRight() while Flyer[4 - i].disappear(): wait() Dev.step(4) if i != 2 and i != 4: for j in range(4): Dev.step(2) Dev.turnLeft() Dev.step(-2) while Flyer[4 - i].disappear(): wait() Dev.step(-2) Dev.turnLeft()我们可以使用字典来存储特殊情况的处理:
special_cases = {2: None, 4: None} for i in range(5): Dev.step(Dev.y - Flyer[4 - i].y) Dev.turnRight() wait_until(lambda: Flyer[4 - i].disappear()) Dev.step(4) if i not in special_cases: for _ in range(4): actions = [ (2, None), (None, 'turnLeft'), (-2, None), (None, lambda: wait_until(lambda: Flyer[4 - i].disappear())), (-2, None), (None, 'turnLeft') ] for steps, action in actions: if steps: Dev.step(steps) if callable(action): action() elif action: getattr(Dev, action)()10. 使用itertools处理复杂循环
Python的itertools模块提供了许多强大的循环工具。让我们优化第十个例子:
原始代码:
for i in range(4): Dev.step(8 - 2 * i) Dev.turnRight() Dev.step(3) for j in range(3): Dev.turnLeft() Dev.step(-2 - i) Dev.turnRight() while Dev.energy < 100: wait() Dev.step(5 + i)我们可以使用itertools.product来处理嵌套循环:
from itertools import product def energy_aware_step(steps): wait_until(lambda: Dev.energy >= 100) Dev.step(steps) for i, j in product(range(4), range(3)): if j == 0: Dev.step(8 - 2 * i) Dev.turnRight() Dev.step(3) Dev.turnLeft() Dev.step(-2 - i) Dev.turnRight() energy_aware_step(5 + i)11. 使用生成器函数分解复杂逻辑
对于特别复杂的循环逻辑,我们可以使用生成器函数来分解。让我们优化第十一个例子:
原始代码:
for i in range(4): Dev.step(2 * (i + 1)) Dev.turnLeft() while Flyer[i].disappear(): wait() Dev.step(5 - i) while Item[i].x != Dev.x or Item[i].y != Dev.y: wait() Dev.step(i - 3) while Flyer[i].disappear(): wait() Dev.step(-2) Dev.turnRight() Dev.step(1) Dev.turnRight()我们可以将每个步骤定义为生成器:
def movement_sequence(i): yield (2 * (i + 1), None) yield (None, 'turnLeft') yield (None, lambda: wait_until(lambda: Flyer[i].disappear())) yield (5 - i, None) yield (None, lambda: wait_until(lambda: Item[i].x == Dev.x and Item[i].y == Dev.y)) yield (i - 3, None) yield (None, lambda: wait_until(lambda: Flyer[i].disappear())) yield (-2, None) yield (None, 'turnRight') yield (1, None) yield (None, 'turnRight') for i in range(4): for steps, action in movement_sequence(i): if steps is not None: Dev.step(steps) if callable(action): action() elif action is not None: getattr(Dev, action)()12. 使用闭包保存状态
在某些情况下,我们需要在循环中保持状态,闭包是一个很好的选择。让我们优化第十二个例子:
原始代码:
for i in range(4): Spaceship.step(6) while not Flyer[3 - i].disappear(): wait() for j in range(3): Spaceship.step(3) Spaceship.turnRight() Spaceship.step(3) Spaceship.turnLeft() Spaceship.step(1) Spaceship.turnRight()我们可以使用闭包来创建有状态的移动函数:
def create_mover(spaceship): def move_sequence(): spaceship.step(6) wait_until(lambda: Flyer[3 - i].disappear()) for _ in range(3): spaceship.step(3) spaceship.turnRight() spaceship.step(3) spaceship.turnLeft() spaceship.step(1) spaceship.turnRight() return move_sequence for i in range(4): mover = create_mover(Spaceship) mover()13. 使用functools.partial简化参数传递
functools.partial可以帮助我们简化重复的函数调用。让我们优化第十三个例子:
原始代码:
for i in range(3): Dev.step(2) for j in range(3): Dev.turnLeft() Dev.step(-2 - i) for j in (-2, 2): while Flyer[2 * i + 1].disappear(): wait() Dev.step(j) while Dev.energy < 100: wait() Dev.turnLeft()我们可以使用partial来预设部分参数:
from functools import partial def step_with_wait(dev, steps, flyer_index=None): if flyer_index is not None: wait_until(lambda: Flyer[flyer_index].disappear()) dev.step(steps) for i in range(3): step_wait = partial(step_with_wait, Dev, flyer_index=2*i+1) Dev.step(2) for _ in range(3): Dev.turnLeft() Dev.step(-2 - i) for steps in (-2, 2): step_wait(steps=steps) wait_until(lambda: Dev.energy >= 100) Dev.turnLeft()14. 使用类方法组织相关操作
对于复杂的对象交互,我们可以使用类方法来组织代码。让我们优化第十四个例子:
原始代码:
for i in range(3): Dev.step(Dev.y - Flyer[2 * i].y - 1) while Flyer[i * 2].disappear(): wait() for j in range(2): Dev.step(2 + i) Dev.turnRight() Dev.step(4 + i) Dev.turnLeft() Dev.step(2) Dev.turnLeft() Dev.step(1) Dev.step(-8)我们可以将相关操作组织为类方法:
class FlyerHandler: @classmethod def handle_flyer(cls, index, dev): dev.step(dev.y - Flyer[2 * index].y - 1) wait_until(lambda: Flyer[index * 2].disappear()) for _ in range(2): dev.step(2 + index) dev.turnRight() dev.step(4 + index) dev.turnLeft() dev.step(2) dev.turnLeft() dev.step(1) dev.step(-8) for i in range(3): FlyerHandler.handle_flyer(i, Dev)15. 使用回调函数处理异步事件
对于需要等待异步事件的场景,回调函数是一个不错的选择。让我们优化第十五个例子:
原始代码:
for i in range(3): Spaceship.step(i + 1) while not Flyer[i * 2].disappear(): wait() Spaceship.step(i + 4) for j in range(3): Spaceship.turnRight() Spaceship.step(i + 2) while not Flyer[i * 2 + 1].disappear(): wait() Spaceship.step(i + 3) Spaceship.turnRight()我们可以使用回调风格的编程:
def on_flyer_disappear(index, callback): wait_until(lambda: Flyer[index].disappear()) callback() def move_sequence(i): Spaceship.step(i + 1) on_flyer_disappear(i * 2, lambda: ( Spaceship.step(i + 4), [( Spaceship.turnRight(), Spaceship.step(i + 2), on_flyer_disappear(i * 2 + 1, lambda: ( Spaceship.step(i + 3), Spaceship.turnRight() )) ) for _ in range(3)] )) for i in range(3): move_sequence(i)16. 使用协程处理复杂流程
Python的协程可以帮助我们管理复杂的控制流程。让我们优化第十六个例子:
原始代码:
for i in range(4): while Flyer[i].disappear(): wait() for j in range(2): Dev.step(5 - i) Dev.turnRight() Dev.step(1) while Dev.energy < 100: wait() Dev.step(4 - i) Dev.turnLeft()我们可以使用协程来组织代码:
async def energy_aware_operation(): while True: wait_until(lambda: Dev.energy >= 100) yield async def movement_coroutine(i): wait_until(lambda: Flyer[i].disappear()) for _ in range(2): Dev.step(5 - i) Dev.turnRight() Dev.step(1) await energy_aware_operation() Dev.step(4 - i) Dev.turnLeft() for i in range(4): movement_coroutine(i).send(None)17. 使用装饰器实现重试逻辑
对于可能失败的操作,我们可以实现自动重试。让我们优化第十七个例子:
原始代码:
for i in range(3): Dev.step(7 - i) Dev.turnRight() while Dev.energy < 100: wait() Dev.step(2 - i) while Flyer[i].disappear(): wait() Dev.step(5) for j in range(3): Dev.turnRight() Dev.step(3) while Dev.energy < 100: wait()我们可以创建重试装饰器:
def retry_on_low_energy(max_retries=3): def decorator(func): def wrapper(*args, **kwargs): retries = 0 while retries < max_retries: try: wait_until(lambda: Dev.energy >= 100) return func(*args, **kwargs) except Exception as e: retries += 1 if retries == max_retries: raise return wrapper return decorator @retry_on_low_energy() def perform_movement(i): Dev.step(7 - i) Dev.turnRight() Dev.step(2 - i) wait_until(lambda: Flyer[i].disappear()) Dev.step(5) for _ in range(3): Dev.turnRight() Dev.step(3) for i in range(3): perform_movement(i)18. 使用属性描述符管理状态
对于需要频繁检查的状态,我们可以使用属性描述符。让我们优化第十八个例子:
原始代码:
for i in range(3): Spaceship.step(i + 2) Spaceship.turnRight() Spaceship.step(i + 2) for j in range(4): Dev.step(j - 4) Dev.turnLeft() while j == 1 and (Item[i].x != Dev.x or Item[i].y != Dev.y): wait() Spaceship.step(2) Spaceship.turnLeft() while Flyer[i].disappear(): wait() Dev.step(2) Spaceship.step(3)我们可以创建状态检查描述符:
class StateChecker: def __get__(self, obj, objtype=None): return obj def __set__(self, obj, value): if hasattr(obj, 'check_conditions'): for condition in obj.check_conditions: wait_until(condition) setattr(obj, '_value', value) class Device: state = StateChecker() def __init__(self, dev): self._dev = dev self.check_conditions = [] def step(self, n): self._dev.step(n) return self def turn(self, direction): getattr(self._dev, f'turn{direction}')() return self dev = Device(Dev) spaceship = Device(Spaceship) for i in range(3): (spaceship.step(i + 2).turn('Right').step(i + 2)) for j in range(4): dev.check_conditions = [] if j == 1: dev.check_conditions.append(lambda: Item[i].x == Dev.x and Item[i].y == Dev.y) (dev.step(j - 4).turn('Left')) spaceship.step(2).turn('Left') dev.check_conditions = [lambda: Flyer[i].disappear()] dev.state = None dev.step(2) spaceship.step(3)19. 使用抽象基类定义接口
对于多种设备类型的操作,我们可以使用抽象基类。让我们优化第十九个例子:
原始代码:
for i in range(3): Spaceship.step(3) while Flyer[i].disappear(): wait() Dev.step(2) Spaceship.step(2) for j in range(4): Dev.turnLeft() Dev.step(-j - 1) while j == 2 and (Item[2 + 4 * i].x != Dev.x): wait()我们可以定义设备接口:
from abc import ABC, abstractmethod class DeviceInterface(ABC): @abstractmethod def step(self, n): pass @abstractmethod def turn(self, direction): pass class SpaceshipController(DeviceInterface): def __init__(self, spaceship): self.spaceship = spaceship def step(self, n): self.spaceship.step(n) def turn(self, direction): getattr(self.spaceship, f'turn{direction}')() class DevController(DeviceInterface): def __init__(self, dev): self.dev = dev def step(self, n): self.dev.step(n) def turn(self, direction): getattr(self.dev, f'turn{direction}')() spaceship = SpaceshipController(Spaceship) dev = DevController(Dev) for i in range(3): spaceship.step(3) wait_until(lambda: Flyer[i].disappear()) dev.step(2) spaceship.step(2) for j in range(4): dev.turn('Left') dev.step(-j - 1) if j == 2: wait_until(lambda: Item[2 + 4 * i].x == Dev.x)20. 使用策略对象处理不同情况
最后,让我们用策略对象来优化第二十个例子:
原始代码:
for i in range(3): Dev.step(5 - i) while Flyer[i].disappear(): wait() Dev.step(5) for j in range(2): Dev.turnLeft() Dev.step(3) Dev.turnRight() Dev.step(i - 2)我们可以为不同的阶段定义不同的策略:
class MovementStrategy: def execute(self, dev, i, j=None): pass class InitialMovement(MovementStrategy): def execute(self, dev, i, j=None): dev.step(5 - i) wait_until(lambda: Flyer[i].disappear()) dev.step(5) class LoopMovement(MovementStrategy): def execute(self, dev, i, j): dev.turnLeft() dev.step(3) dev.turnRight() dev.step(i - 2) strategies = [ (InitialMovement(), None), *[(LoopMovement(), j) for j in range(2)] ] for i in range(3): for strategy, j in strategies: strategy.execute(Dev, i, j)通过这些重构技巧,我们不仅使ICode训练场中的代码更加Pythonic和易于维护,更重要的是培养了编写高质量Python代码的思维方式和习惯。这些技巧可以直接应用到实际项目开发中,帮助你写出更优雅、更高效的Python代码。