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

Tkinter继承下Scrabble GUI开发的AttributeError问题排查

问题:Tkinter开发Scrabble GUI时出现AttributeError: Frame has no attribute letterBag

问题详情

用Tkinter开发Scrabble图形界面,运行代码时始终抛出AttributeError: Frame has no attribute letterBag。对比两段代码:无Tkinter GUI的版本运行正常,包含GUI的版本报错。

我猜想问题原因是:Tkinter组件初始化时传入的是Frame实例而非App类引用,导致LetterRack的master属性指向Frame,而Frame没有仅属于App类的letterBag属性。这个猜想是否正确?

无Tkinter GUI的正常代码

import random as r

class App:
    def __init__(self):
        self.letterBag = LetterBag()
        self.letterRack = LetterRack(self)

class LetterBag:
    def __init__(self):
        self.tilesLeft = 10
        self.tileList = ["" for x in range(10)]
        
    def remove_tile(self):
        self.tilesLeft -= 1
        return self.tileList.pop(r.randint(0,self.tilesLeft))

class LetterRack:
    def __init__(self,master):
        self.master = master #Master would be used for Tkinter widget instantiation
        self.tiles = []
        self.replenish_tiles()

    def replenish_tiles(self):
        while self.master.letterBag.tilesLeft != 0 and len(self.tiles) < 7:
            self.add_tile()
        
    def add_tile(self):
        self.tiles.append("3")

带Tkinter GUI的报错代码

import random as r
import tkinter as tk

class App(tk.Tk): # Main class for the whole game
    def __init__(self):
        super().__init__() #Intiate tk.Tk() window
        self.configure(bg="#50c878")
        self.title("Scrabble")
        self.resizable(False,False)
        self.containerFrame1 = tk.Frame(master=self,bg="#50c878",width=43,height=100)
        self.containerFrame2 = tk.Frame(master=self,bg="#50c878",width=63,height=100)
        self.letterBag = LetterBag()
        self.letterRack = LetterRack(self.containerFrame1)
        self.letterRack.grid(row=1,column=0,pady=(1,2),padx=(2,1))
        self.containerFrame1.grid(row=0,column=0)
        self.containerFrame2.grid(row=0,column=1)
        self.mainloop()

class LetterBag:
    def __init__(self):
        self.tilesLeft = 10
        self.tileList = ["" for x in range(10)]
        
    def remove_tile(self):
        self.tilesLeft -= 1
        return self.tileList.pop(r.randint(0,self.tilesLeft))

class LetterRack(tk.Frame): #Class defining letter rack frame
    def __init__(self,master):
        super().__init__(master=master,bg="#FFFFFF",width=40,height=40)
        self.master = master
        self.tiles = []
        self.replenish_tiles()

    def replenish_tiles(self):
        while self.master.letterBag.tilesLeft != 0 and len(self.tiles) < 7:
            self.add_tile()
    
    def add_tile(self):
        pass

window = App()

解答

你的猜想完全正确。

问题根源

  • 无GUI版本中,LetterRack初始化时传入的是App类的实例(self),所以self.master指向App对象,能正常访问App的letterBag属性。
  • 带GUI版本中,你创建LetterRack时传入的是self.containerFrame1——这是一个tk.Frame实例,并非App类的实例。当replenish_tiles方法调用self.master.letterBag时,master是Frame对象,它没有letterBag属性,因此抛出错误。

修复方案

方案1:直接传入App实例并指定父容器

修改App类中创建LetterRack的代码,传入App实例:

self.letterRack = LetterRack(self)

然后修改LetterRack类,保存App实例引用,并将containerFrame1作为Tkinter组件的父容器:

class LetterRack(tk.Frame):
    def __init__(self, app):
        self.app = app  # 保存App实例,用于访问letterBag
        super().__init__(master=app.containerFrame1, bg="#FFFFFF", width=40, height=40)
        self.tiles = []
        self.replenish_tiles()

    def replenish_tiles(self):
        while self.app.letterBag.tilesLeft != 0 and len(self.tiles) < 7:
            self.add_tile()

方案2:通过Frame的层级向上访问App实例

利用Tkinter组件的层级关系,containerFrame1的master就是App实例,因此可以通过self.master.master访问App对象:
修改LetterRack的replenish_tiles方法:

def replenish_tiles(self):
    while self.master.master.letterBag.tilesLeft != 0 and len(self.tiles) < 7:
        self.add_tile()

这种方式无需修改初始化参数,但依赖组件层级结构,后续布局调整可能引发问题,推荐优先使用方案1。

内容的提问来源于stack exchange,提问作者Shaken not Stirred

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 14:18:10