Discord.py中View类实例化时__init__赋值被按钮忽略的问题
我尝试用Discord.Py创建标签可动态变化的按钮,为此写了一个discord.ui.View的子类,__init__方法接收name和id参数。实例化时断点显示self.modelName和self.modelId已经成功赋值,但生成的按钮完全忽略这些值,用的是类定义的默认值。
类定义代码:
class AlphaStrikeCardButtons(discord.ui.View): modelName = "supernova 2" modelId = "3133" #defining constructor def __init__(self,name,id): super().__init__() self.modelName = name self.modelId = f'asc{id}' @discord.ui.button(label=modelName, custom_id=modelId, style=discord.ButtonStyle.blurple) #@discord.ui.button(label='supernova 5', custom_id='asc8132', style=discord.ButtonStyle.blurple) async def alphaStrikeCard(self, interaction: discord.Interaction, button: discord.ui.Button): cardPath = f'./tmp/asc{modelId}.png' #If it already exists, we don't need to download it again if os.path.isfile(cardPath) == False: cardUrl = f'http://masterunitlist.info/Unit/Card/{self.modelId}' print(cardUrl) urlretrieve(cardUrl,cardPath) cardFile = discord.File(cardPath,filename=f'{self.modelId}.png') await interaction.response.send_message(file=cardFile, ephemeral=False) self.stop()
调用代码:
ASCB = AlphaStrikeCardButtons(name=modelName,id=modelId) await interaction.response.send_message("Please click the button:",ephemeral=True,view=ASCB)
传入的modelName是'Supernova 5'、modelId是'8132',但按钮显示标签是"Supernova 2",custom_id是asc3133(类默认值)。如果不设置类的默认值(设为None),会报错:
In data.components.0.components.0.label: This field is required
明明传了参数,却提示未传入值,请问为什么Python会忽略__init__中设置的变量值,怎么确保使用传入的参数?
原因分析
@discord.ui.button装饰器是在类定义阶段执行的,此时__init__方法还未被调用,装饰器里的label=modelName和custom_id=modelId只会绑定类级别的默认值,不会等到实例化时读取self上的属性。所以不管你在__init__里给self.modelName和self.modelId赋什么值,按钮的属性已经在类创建时就固定了。
另外,按钮回调里的cardPath = f'./tmp/asc{modelId}.png'也存在问题,这里的modelId是类级变量,不是实例的self.modelId,会导致路径错误。
解决方法
不要用装饰器定义按钮,而是在__init__方法里动态创建按钮,这样就能用实例化时传入的参数:
修改后的类代码:
class AlphaStrikeCardButtons(discord.ui.View): def __init__(self, name, id): super().__init__() self.modelName = name self.modelId = f'asc{id}' # 动态创建按钮,使用实例属性 button = discord.ui.Button( label=self.modelName, custom_id=self.modelId, style=discord.ButtonStyle.blurple ) # 绑定回调函数 button.callback = self.alphaStrikeCard # 将按钮添加到View中 self.add_item(button) async def alphaStrikeCard(self, interaction: discord.Interaction, button: discord.ui.Button): # 这里要用self.modelId cardPath = f'./tmp/{self.modelId}.png' if not os.path.isfile(cardPath): cardUrl = f'http://masterunitlist.info/Unit/Card/{self.modelId}' print(cardUrl) urlretrieve(cardUrl, cardPath) cardFile = discord.File(cardPath, filename=f'{self.modelId}.png') await interaction.response.send_message(file=cardFile, ephemeral=False) self.stop()
关键改动说明
- 移除类级别的
modelName和modelId默认值,直接在__init__里初始化实例属性。 - 在
__init__中手动创建discord.ui.Button实例,传入self.modelName和self.modelId作为按钮属性。 - 把原来的回调函数
alphaStrikeCard绑定到按钮的callback属性上,再用self.add_item()把按钮添加到View里。 - 回调函数里的路径计算改用
self.modelId,确保使用实例的属性值。
这样实例化View时,按钮的标签和custom_id就会使用传入的参数,不会再用类的默认值了。
内容的提问来源于stack exchange,提问作者Keming Lamurnogo

