为何super()可修改父类字典类变量却无法递增数值类变量?
运行以下Python代码片段时出现异常:
class Magical_SportsPerson: database = {} playerNo = 0 def __init__(self,name,team,jerseyNo): self.name = name self.team = team self.jerseyNo = jerseyNo def __str__(self): return "Name:{}\nTeam:{}\nJerseyNo:{}".format(self.name,self.team, self.jerseyNo) class Quidditch_Player(Magical_SportsPerson): def __init__(self,name,team,jerseyNo,goals = None,retirement_date = "Not yet retired"): super().__init__(name,team,jerseyNo) super().playerNo += 1 # 此处报错 self.goals = goals self.retirement_date = retirement_date # 创建玩家ID格式:playerNo + 名字首字母 + 姓氏首字母 + 球衣号 self.playerID = str(super().playerNo) + self.name[0] + self.name[self.name.index(" ") + 1] + str(self.jerseyNo) # 玩家详情列表 self.playerDetails = [self.name, self.team, self.jerseyNo, self.goals, self.retirement_date] # 添加到数据库 super().database[self.playerID] = self.playerDetails def __str__(self): details = Magical_SportsPerson.__str__(self) + "\nGoals:{}".format(self.goals) + "\nRetirement Date:{}".format(self.retirement_date) return details @classmethod def createPlayer(cls,name,team,jerseyNo,goals = None ,retirement_date = "Not yet retired"): return cls(name,team,jerseyNo,goals,retirement_date)
创建实例时:
p1 = Quidditch_Player("Harry Potter","Gryffindor", 8, 523) print("------Details of the player------") print(p1)
抛出错误:AttributeError: 'super' object has no attribute 'playerNo'。但改用Magical_SportsPerson.playerNo += 1就能正常运行,同时super().database[self.playerID] = self.playerDetails可以正常修改父类的database类变量。同为父类Magical_SportsPerson的类变量,为何super()对两者的操作结果不同?
核心原因是赋值操作(+=属于赋值类操作)和取值/修改容器内部元素的操作,对super()对象的行为影响完全不同:
针对
super().database[self.playerID] = self.playerDetails:
这不是给super()对象赋值属性,而是先通过super()找到父类的database字典对象,再修改这个字典的内部键值对。super()在这里仅负责查找父类属性,最终操作的是找到的字典实例本身,不会触发属性赋值逻辑,因此能正常工作。针对
super().playerNo += 1:+=本质是playerNo = playerNo + 1,属于属性赋值操作。当对super()对象进行属性赋值时,Python不会将值赋给父类的类变量,反而会尝试在当前类(Quidditch_Player)或实例上创建新属性。但在赋值前的取值阶段,super()找不到playerNo(super()本身不存储属性,仅作为查找链),于是直接抛出AttributeError。
而直接使用Magical_SportsPerson.playerNo += 1时,是明确指定修改父类的类变量,绕过了super()的属性查找与赋值逻辑,因此能正确修改父类的playerNo。
补充说明:如果只是读取super().playerNo(比如print(super().playerNo)),是可以正常获取父类类变量值的,但一旦涉及赋值操作,super()的行为就会改变——赋值会绑定到当前上下文(当前类或实例),而非父类。
内容的提问来源于stack exchange,提问作者Arif Hamim

