Python:继承与组合

Python:继承与组合,python,inheritance,composition,Python,Inheritance,Composition,我正在使用Python中的两个类,其中一个应该允许将另一个类中任意数量的对象作为子对象,同时将这些子对象的清单作为属性保存。对于这种亲子关系的情况,继承似乎是一个显而易见的选择,但我所得到的却是一个组合的例子。以下是简化代码: 类父级: 定义初始化(self、firstname、lastname): self.firstname=firstname self.lastname=lastname self.kids=[] def havechild(自我,名字): 打印(self.firstnam

我正在使用Python中的两个类,其中一个应该允许将另一个类中任意数量的对象作为子对象,同时将这些子对象的清单作为属性保存。对于这种亲子关系的情况,继承似乎是一个显而易见的选择,但我所得到的却是一个组合的例子。以下是简化代码:

类父级:
定义初始化(self、firstname、lastname):
self.firstname=firstname
self.lastname=lastname
self.kids=[]
def havechild(自我,名字):
打印(self.firstname,“正在生孩子”)
self.kids.append(Child(self,firstname))
班级儿童(家长):
def uuu init uuuu(自我、父母、名字):
self.parent=parent
self.firstname=firstname
self.lastname=parent.lastname
因此,基本上,虽然让Child()从Parent()继承似乎有直观的意义,但删除继承并不会改变任何东西。离开Child(Parent)而不仅仅是class Child()的唯一好处是,如果我需要向Parent添加更多希望Child继承的方法。使用self.parent=parent,我已经可以访问父对象的任何其他未来属性


是否有其他方法可以使用纯继承而不是将父实例传递到子构造函数(组合)?

从父实例继承子实例或从子实例继承父实例绝对不好

正确的方法是创建一个基类,比如Person并从中继承子类和父类。 这样做的一个优点是消除代码重复,目前只有firstname/lastname字段复制到两个对象中,但可能有更多数据或其他方法,如
get\u name()
来处理此数据

以下是一个例子:

班级人员:
定义初始化(self、firstname、lastname):
self.firstname=firstname
self.lastname=lastname
def get_名称(自身):
返回f“{self.firstname}{self.lastname}”
班级家长(人):
定义初始化(self、firstname、lastname):
超级()
self.kids=[]
def havechild(自我,名字):
打印(self.firstname,“正在生孩子”)
self.kids.append(Child(self,firstname))
班级儿童(人):
def uuu init uuuu(自我、父母、名字):
super()
self.parent=parent
另一种方法是不继承,但只有一个Person对象(vs Parent和Child)。 可以将跟踪家庭状态和父/子对象的功能移动到另一个对象中

这种方法的一个优点是,您遵循对象并保持对象简单,每个对象只做一件事

以下是一个例子:

从集合导入defaultdict
班长:
定义初始化(self、firstname、lastname):
self.firstname=firstname
self.lastname=lastname
def get_名称(自身):
返回f“{self.firstname}{self.lastname}”
类FamilyRegistry(对象):
定义初始化(自):
self.kids=defaultdict(列表)
def登记出生(本人、父母、子女姓名):
打印(parent.firstname,“正在生孩子”)
child=人(child\u name,parent.lastname)
self.kids[parent.lastname].append(child)
返回儿童
def print_儿童(自我、个人):
children=self.children[person.lastname]
如果len(子项)==0:
打印({}没有子项“%person.get\u name())
返回
对于儿童中的儿童:
打印(child.get_name())
它的工作原理如下:

joe=Person('joe','Black')
吉尔=人(“吉尔”、“怀特”)
注册表=家庭注册表()
登记处。登记出生(乔,小乔)#乔有孩子了
登记处。登记出生(乔,蒂娜)#乔有孩子了
注册表。打印儿童(joe)#joe Junior Black
#蒂娜·布莱克
registry.print_children(吉尔)#吉尔·怀特没有孩子

首先,我认为你把事情弄糊涂了。正如您自己提到的,继承用于希望继承父类的性质,然后修改该行为并扩展它的类

在您的示例中,子对象从父对象继承两件事情。构造函数
\uuuu init\uuuu
havechild
。您正在重写构造函数,
havechild
方法不应该起作用,因为没有可附加新子元素的子元素成员列表。此外,您似乎不打算让孩子生孩子

尽管如此,您可能实际上想问一个不同的问题,例如合成与聚合。有一种设计选择,比如继承与组合,这实际上对Python来说可能特别有趣,但它主要询问您是希望通过复制独立父类的行为(继承)来重用代码,还是希望分离不同的类行为颗粒(缺少更好的词)然后创建由这些颗粒组成的类


这本书也很有名,有一个很好的不同设计模式的目录。

不,你做得对。如果仅仅为了在对象之间共享方法而通过继承在对象之间引入耦合是不好的,除非您希望它们具有“is-a”,即使这样,您也可能希望改为使用“mixin”路径。继承用于“is-a”关系。孩子是父母吗?不一定。组合是为“has-a”关系而设计的。子对象有父对象(父对象有子对象)。如果您有一个person类,那么您将使用继承,那么子类就是person,因此子类将从person继承。如果您需要这两个类共享一些常用方法,那么您可以始终从person类中对这两个类进行子类化