Python面向对象编程:组合
我一直在学习如何在python编程中实现组合,但我很难理解为什么它比继承更受欢迎 例如,以下是我迄今为止的代码:Python面向对象编程:组合,python,oop,composition,Python,Oop,Composition,我一直在学习如何在python编程中实现组合,但我很难理解为什么它比继承更受欢迎 例如,以下是我迄今为止的代码: class Particle: # Constructor (public) def __init__(self, _name, _charge, _rest_energy, _mass, _velocity): # Attributes (private) self.__name = _name self.__charge =
class Particle:
# Constructor (public)
def __init__(self, _name, _charge, _rest_energy, _mass, _velocity):
# Attributes (private)
self.__name = _name
self.__charge = _charge
self.__restEnergy = _rest_energy
self.__mass = _mass
self.__velocity = _velocity
# Getter functions (public)
def getName(self):
return self.__name
def getCharge(self):
return self.__charge
def getRestEnergy(self):
return self.__restEnergy
def getMass(self):
return self.__mass
def getVelocity(self):
return self.__velocity
# Setter procedures (public)
def setName(self, _name):
self.__name = _name
def setCharge(self, _charge):
self.__charge = _charge
def setRestEnergy(self, _rest_energy):
self.__restEnergy = _rest_energy
def setMass(self, _mass):
self.__mass = _mass
def setVelocity(self, _velocity):
self.__velocity = _velocity
class Quark:
# Constructor (public)
def __init__(self, _name, _charge, _strangeness):
# Attributes (private)
self.__name = _name
self.__charge = _charge
self.__strangeness = _strangeness
# Getter functions (public)
def getName(self):
return self.__name
def getCharge(self):
return self.__charge
def getStrangeness(self):
return self.__strangeness
class Hadron:
# Constructor (public)
def __init__(self, _name, _charge, _rest_energy, _mass, _velocity, _quarks):
# Attributes (private)
self.__particle = Particle(_name, _charge, _rest_energy, _mass, _velocity)
self.__quarks = _quarks
# Getter functions (public)
def getParticle(self):
return self.__particle
def getQuark(self):
return self.__quarks
def getStrangeness(self):
_quarks = self.__quarks
_strangeness = 0
for _quark in _quarks:
_strangeness += _quark.getStrangeness()
return _strangeness
def getRelCharge(self):
_quarks = self.__quarks
_relCharge = 0
for _quark in _quarks:
_relCharge += _quark.getCharge()
return _relCharge
def getName(self):
return self.__particle.getName()
def getCharge(self):
return self.__particle.getCharge()
def getRestEnergy(self):
return self.__particle.getRestEnergy()
def getMass(self):
return self.__particle.getMass()
def getVelocity(self):
return self.__particle.getVelocity()
# Setter functions (public)
def setName(self, _name):
self.__particle.setName(_name)
def setCharge(self, _charge):
self.__particle.setCharge(_charge)
def setRestEnergy(self, _rest_energy):
self.__particle.setRestEnergy(_rest_energy)
def setMass(self, _mass):
self.__particle.setMass(_mass)
def setVelocity(self, _velocity):
self.__particle.setVelocity(_velocity)
我不确定我是不是搞错了,但当我可以从粒子类继承时,这似乎是难以置信的浪费
我做错什么了吗?你使用哪一种取决于你试图建立什么样的关系 组合并不总是更好的选择。“组合重于继承”经常被重复,因为继承经常被滥用,因为它减少了您需要编写的代码量。然而,这完全是错误的动机,你的决定是建立在这个基础上的 如果您有两个类,
A
和B
,则大致的一般指南如下:
- 如果
是一个B
,您可能想要继承A
- 如果
有一个B
,您可能需要合成A
在你的例子中,从我极其有限的粒子物理学知识来看,
强子是一个粒子,所以继承可能更合适。强子不包含/有粒子,所以我认为你试图通过在这里强制合成来对抗粒子。Carcigenicate已经提供了一个很好的答案。我只是想补充一下这个部分:
我很难理解为什么(组合)比继承更受欢迎
实际上,它是关于组合/委托的,而不仅仅是组合(组合本身并不提供相同的特性)。关键是继承实际上有两个目的:子类型化和实现重用
子类型表示“是a”关系-如果B是a的(适当的)子类型,则可以在任何可以使用a的地方使用B。实际上,Liskov替换原则反过来说:“如果任何接受a的代码都可以接受B,则B是a的适当子类型”。请注意,这并没有说明继承和实现的任何内容,而且这些都不是子类型化所必需的(在理论层面上)
现在,对于静态类型语言,您必须使用继承来进行子类型化,句号-如果至少有任何实现需要重用,您最终会得到实现重用作为奖励
动态类型化的OTHO-Python不需要继承子类型(当然,只要使用对象的代码不进行任何类型检查)——只要有一个兼容的接口就足够了。因此,在Python中,继承主要与实现重用有关
现在,从技术上讲,通过继承实现重用是一种组合/委托的形式——您的对象是它自己的类的实例,也是它所有超类的实例,并且无论在您的实例本身或它的类上没有解析的属性都将在父类上查找。“手动”组合/委派的主要区别在于继承受到更严格的限制——例如,您不能在运行时更改委派对象,也不能基于每个实例更改委派对象(好吧……在Python中,从技术上讲,您实际上可以在运行时更改实例的类,但实际上这是一个非常糟糕的主意,而且从来没有像预期的那样工作过——在这里,就是xD)
因此,wrt/实现重用、继承是一种受限的、主要是静态的组合/委托形式。这对于相当多的用例来说是好的,通常是框架或多或少需要继承的抽象基类等,但是其他一些情况可以用更动态的解决方案更好地解决(典型的例子是状态和策略设计模式,但还有很多其他模式)
此外,即使继承仅用于实现重用,继承仍然意味着一种“是一种”关系——即使子类不是它的基的适当子类型(任何不兼容都会破坏适当的子类型,并且没有任何东西阻止您使用不兼容的签名更改某些子类方法),Python也没有任何概念“私有继承”,您的子类将公开其继承的所有接口,这不一定是您想要的(实际上,在执行实现重用时,通常是您不想要的)。当然,如果您决定更改要重用的实现,那么……继承引入了更强的耦合(这是一种轻描淡写的说法)而不是组成/授权
一个典型的“初学者错误”示例是从某个内置集合类型继承(比如说list
),并尝试将其“限制”到其特定需要。这从来没有真正按照预期工作,通常需要比使用组合/委派更多的工作。然后他们意识到这一点(仍然是示例)对于他们自己的用例来说,OrderedDict会是一个更好的基础,然后他们会遇到一个问题,即客户机代码现在依赖于继承的列表接口…从一开始就使用组合/委派可以避免很多痛苦-通过将接口限制在相关特性上泄漏继承的接口,从而保持“实现重用”的真实性:客户机代码永远不会知道的实现细节
核心问题实际上是太多非常糟糕的“OO 101”文本将继承作为关键OO特性之一(事实并非如此——真正的“OO关键特性”是封装——不要与数据隐藏BTW和基于类型的多态调度混淆),导致初学者试图过度使用继承,却从未意识到还有其他更好的解决方案
长话短说:像任何其他“黄金法则”一样,偏爱合成