Python多继承下同名实例属性引发的异常问题咨询
嗨,这绝对不是Python的bug,而是你没搞清楚Python多继承下实例属性的工作方式~
先帮你捋清楚问题出在哪:当你创建Animal实例的时候,初始化过程是这样的:
- 先调用
Legs.__init__(self, legs_amount),这时候给同一个实例对象的self.amount赋值成腿的数量; - 接着调用
Eyes.__init__(self, eyes_amount),又把这个self.amount改成了眼睛的数量!
所以到最后,self.amount其实是眼睛的数量——这就是为什么cat.legsInfo()返回2(猫的眼睛数量),spider.legsInfo()返回6(蜘蛛的眼睛数量):因为legsInfo方法取的是self.amount,而这个值已经被Eyes的初始化覆盖了。
你觉得“不同类所以不需要共享”,但这里的核心问题不是类之间共享属性,而是同一个实例对象的属性被多次赋值覆盖了。Python里的实例属性是存在实例自己的命名空间(__dict__)里的,所有父类的__init__都是在操作同一个实例,所以用相同的属性名必然会被后面的赋值覆盖,这是Python的正常行为,完全不是bug。
那怎么实现你想要的“属性只属于当前类”的效果呢?有几种常用方案:
1. 重命名属性(你已经用到的方法)
像你那样把属性名改成legsAmount和eyesAmount,从根源上避免名称冲突,简单直接。
2. 使用Python的名称修饰(“私有”属性)
在属性名前面加两个下划线(比如self.__amount),Python会自动对这个属性名做“名称修饰”:把Legs类里的self.__amount转换成_Legs__amount,Eyes类里的转换成_Eyes__amount,这样即使两个父类用了相同的“私有”属性名,也不会冲突。修改后的代码如下:
class Legs: def __init__(self, amount): self.__amount = amount def legsInfo(self): return f"{self.__amount} legs" class Eyes: def __init__(self, amount): self.__amount = amount def eyesInfo(self): return f"{self.__amount} eyes" class Animal(Legs, Eyes): def __init__(self, name, legs_amount, eyes_amount): self.name = name Legs.__init__(self, legs_amount) Eyes.__init__(self, eyes_amount) def legsInfo(self): return super().legsInfo() def eyesInfo(self): return super().eyesInfo() # Objects cat = Animal("Tom", 4, 2) spider = Animal("Webster", 8, 6) # Test output print(cat.legsInfo()) # 4 legs 正确 print(cat.eyesInfo()) # 2 eyes 正确 print(spider.legsInfo()) # 8 legs 正确 print(spider.eyesInfo()) # 6 eyes 正确
3. 用组合代替多继承(更推荐的设计方式)
其实从面向对象的逻辑来说,“动物有腿和眼睛”是组合关系(has-a),而不是继承关系(is-a)。你可以让Animal类持有Legs和Eyes的实例,这样完全不会有属性冲突的问题,代码逻辑也更清晰:
class Legs: def __init__(self, amount): self.amount = amount def legsInfo(self): return f"{self.amount} legs" class Eyes: def __init__(self, amount): self.amount = amount def eyesInfo(self): return f"{self.amount} eyes" class Animal: def __init__(self, name, legs_amount, eyes_amount): self.name = name self.legs = Legs(legs_amount) self.eyes = Eyes(eyes_amount) def legsInfo(self): return self.legs.legsInfo() def eyesInfo(self): return self.eyes.eyesInfo() # Objects cat = Animal("Tom", 4, 2) spider = Animal("Webster", 8, 6) # Test output print(cat.legsInfo()) # 4 legs 正确 print(cat.eyesInfo()) # 2 eyes 正确 print(spider.legsInfo()) # 8 legs 正确 print(spider.eyesInfo()) # 6 eyes 正确
总结一下:这是Python的正常行为,不是bug。你可以通过重命名属性、使用名称修饰或者组合的方式来解决属性冲突的问题,其中组合的方式更符合面向对象的设计原则哦~
备注:内容来源于stack exchange,提问作者Vincent

