Python3.10中跨类调用类实例方法报错如何解决
问题背景
- Python版本:3.10
- 场景:pygame开发时,尝试在Game类中调用Button类定义的
is_clicked()方法实现按钮点击检测,运行时抛出如下错误:
if Button.is_clicked(event.pos): TypeError: Button.is_clicked() missing 1 required positional argument: 'mouse_pos'
- 目标:在Game类的
event_check()事件检测逻辑中,正确调用点击检测方法,覆盖所有已创建的Button实例的点击判断。 - 原问题代码:
import pygame, sys class Game: def __init__(self, resolution: tuple, fps: int = 24): pygame.init() pygame.font.init() self.clock = pygame.time.Clock() self.resolution = resolution self.screen = pygame.display.set_mode(self.resolution, pygame.RESIZABLE) self.font = pygame.font.SysFont("Arial", 20) self.fps = fps def get_clock(self): return self.clock def get_screen(self): return self.screen def get_resolution(self): return self.resolution def get_font(self): return self.font def tick_screen(self): pygame.display.flip() self.clock.tick(self.fps) def event_check(self): for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.MOUSEBUTTONDOWN: # problem here :( if Button.is_clicked(event.pos): #REFERENCE THE BUTTON INSTANCE HERE print("Button clicked!") class Rect: # Constructor def __init__(self, x, y, width, height, color): self.x = x self.y = y self.width = width self.height = height self.color = color # Draw the rectangle def draw(self, screen): pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height)) class NarratingRect(Rect): # Constructor def __init__(self, x, y, width, height, color, font, text): super().__init__(x, y, width, height, color) self.font = font self.text = text # Draw the rectangle def draw(self, screen): super().draw(screen) text = self.font.render(self.text, True, (0, 0, 0)) screen.blit(text, (self.x, self.y)) class Button(Rect): # Constructor def __init__(self, x, y, width, height, color, font, text): super().__init__(x, y, width, height, color) self.font = font self.text = text # Draw the button def draw(self, screen): super().draw(screen) text = self.font.render(self.text, True, (0, 0, 0)) text_rect = text.get_rect() text_rect.center = (self.x + self.width // 2, self.y + self.height // 2) screen.blit(text, text_rect) # Check if the button is clicked def is_clicked(self, mouse_pos): if self.x < mouse_pos[0] < self.x + self.width and self.y < mouse_pos[1] < self.y + self.height: return True else: return False
错误原因
is_clicked()是Button类的实例方法,按照Python的方法调用规则:
- 当通过
具体按钮实例.is_clicked(参数)的形式调用时,Python会自动把当前按钮实例绑定给方法的第一个形参self,开发者只需要传入mouse_pos参数即可 - 当前直接通过类名
Button.is_clicked(event.pos)调用,传入的event.pos会被直接绑定给self形参,要求传入的mouse_pos没有拿到对应值,就会抛出缺参数的错误。
除此之外,原代码没有维护所有已创建Button实例的容器,即便解决了参数传递问题,也无法实现“检测所有按钮点击”的需求。
修复方案
- 给Game类增加实例列表,统一管理所有按钮
在Game类的__init__方法中新增一个列表属性,专门存储所有创建好的Button实例:
def __init__(self, resolution: tuple, fps: int = 24): pygame.init() pygame.font.init() self.clock = pygame.time.Clock() self.resolution = resolution self.screen = pygame.display.set_mode(self.resolution, pygame.RESIZABLE) self.font = pygame.font.SysFont("Arial", 20) self.fps = fps # 新增:按钮实例容器 self.buttons = []
- 修改事件检测逻辑,遍历按钮逐个检测
把event_check()中鼠标按下的判断逻辑改成遍历所有已注册的按钮,通过实例调用is_clicked()方法:
def event_check(self): for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.MOUSEBUTTONDOWN: mouse_pos = event.pos # 遍历所有按钮检测点击 for btn in self.buttons: if btn.is_clicked(mouse_pos): print(f"按钮[{btn.text}]被点击") # 此处编写对应按钮的点击业务逻辑即可
- 注册按钮实例
后续每创建一个Button实例,主动将其添加到Game实例的buttons列表中即可纳入点击检测范围,示例:
# 初始化游戏 game = Game((1280, 720)) # 创建按钮 start_btn = Button(540, 300, 200, 80, (120,220,120), game.get_font(), "开始游戏") exit_btn = Button(540, 420, 200, 80, (220,120,120), game.get_font(), "退出游戏") # 注册按钮到检测列表 game.buttons.append(start_btn) game.buttons.append(exit_btn)
补充:如果硬要通过类名调用实例方法,需要手动传入按钮实例作为第一个参数,格式为
Button.is_clicked(具体按钮实例, event.pos),但这种写法不符合面向对象设计习惯,也无法便捷实现多按钮遍历检测,不推荐使用。
内容的提问来源于stack exchange,提问作者Nyktein
相关产品推荐
相关产品推荐

