继承与合成

在阅读本文之前,你只需要记住我这一句:

大部分使用继承的场合后可以用合成来取代,而多重继承则需要不惜一切地避免之。

首先我详细介绍一下继承的概念:

‘‘‘

什么是继承?

继承就是用来指明一个类的大部分或全部功能都是从一个父类中获得的。

比如class Foo(Bar):创建一个叫Foo的类,并让它继承Bar。

当你这样写的时候,Bar的实例所具有的功能都工作在Foo的实例上。

当你这么做的时候,父类和子类有三种交互方式:

1.子类上的动作完全等同于父类上的动作。

2.子类上的动作完全覆盖了父类上的动作。

3.子类上的动作部分替换了父类上的动作。

‘‘‘

‘‘‘

①隐式继承:

当你在父类里定义了一个函数但没有在子类中定义的例子时,会发生隐式继承。

‘‘‘

class Parent(object):

def implicit(self):

print("PARENT implicit()")

class Child(Parent):

pass            #类Child里面没有定义任何细节,但是从Parent父类继承所有的行为。

dad=Parent()

son=Child()

dad.implicit()

son.implicit()

‘‘‘

我们可以看见,输出结果都是:PARENT implicit()。

说明了子类Child继承了父类Parent的所有功能,这就是隐式继承。

方法是创建一个子类的时候,括号内填入父类函数,不定义任何东西,就能完成继承父类的功能。

优点:需要很多类时,这样可以避免重复写很多代码。

‘‘‘

‘‘‘

②显式覆盖:

有时候,需要让子类的函数有一个不同的行为,显然这是隐式继承做不到的差事。

在子类中Child定义一个相同名称的函数就可以了。

‘‘‘

class Parent(object):

def override(self):

print("PARENT override()")

class Child(Parent):

def override(self):

print("CHILD pverride()")

dad=Parent()

son=Child()

dad.override()

son.override()

‘‘‘

从输出结果来看:

PARENT override()

CHILD pverride()

虽然子类Child继承了父类Parent的功能,但并不代表输出结果就应该一样。

dad.override()的打印结果应该没问题,这是Parent父类的一个实例。

son.override(),son是从子类Child的一个实例,虽然它继承了父类Parenet的功能,

但是类Child的实例是由类Child下定义的函数来执行,虽然函数名一样,但是函数内的变量属于局部变量大家应该知道。

当类Child下没有定义任何的函数时,这时候才去继承父类Parenet里面的函数来执行,这个过程就叫隐式继承。

大家弄清楚隐式继承和显式覆盖的区别了吗?

‘‘‘

‘‘‘

③在运行前或运行后替换:

这是使用继承的第三种方法。这种情况下,你想在父类中定义的内容运行之前或者之后在修改行为。

‘‘‘

class Parent(object):

def altered(self):

print("PARENT altered()")

class Child(Parent):

def altered(self):

print("CHILD,BEFORE PARENT altered()")

super(Child,self).altered()

print("CHILD,AFTER PARENT altered()")

dad=Parent()

son=Child()

dad.altered()

son.altered()

‘‘‘

我们先来看一下输出的结果:

PARENT altered()

CHILD,BEFORE PARENT altered()

PARENT altered()

CHILD,AFTER PARENT altered()

第一行、第二行和第四行的输出结果应该没有什么疑问,现在重点分析第三行的输出结果。

super(Child,self).altered(),这一行调用了父类的函数,我们先分析以下super()的作用:

super() 函数是用于调用父类(超类)的一个方法,这个方法是用来解决多重继承的问题,直接调用父类。

那么我们可以把这句话解读为:

用Child和self这两个参数调用super,找到Child的父类Parent,把子类Child实例的对象转换为父类Parent的实例对象,

然后在此返回的基础上调用altered函数。到这里Parent.altered就会被打印,从而打印出父类里的信息。

我这么解释是不是比较通俗易懂?希望对你有帮助!

‘‘‘

‘‘‘

④为什么要用super():

其实我在上面已经隐隐约约地给出了答案,就是要应对一个叫“多重继承”的麻烦东西。

多重继承是指定义的类继承了多个类,就像这样:

class SuperFun(Child,BadStuff):

pass

也就是说,创建一个叫SuperFun的类,让它同时继承Child和BadStuff。

这样一来一旦在SuperFun的实例上调用任何隐式动作,Python就要回到类的层次结构中以固定的次序去检查,这样会很麻烦吧。

所以super()就在这个形势下诞生了,再也不用担心把继承关系弄糟,因为会正确快速地找到需要的函数。

super()和__init__搭配使用:

这是最常见的super()与基类的__init__函数搭配使用,在子类里做了一些事情,然后完成对父类的初始化。

class Child(Parent):

def __init__(self,stuff):

self.stuff=stuff

super(Child,self).__init__()

‘‘‘

‘‘‘

⑤合成:

继承虽然是一种有用的技术,不过还有一种实现相同功能的方法,就是直接使用别的类和模块,而非依赖于继承。

‘‘‘

class Other(object):

def override(self):

print("OTHER override()")

def implicit(self):

print("OTHER implicit()")

def altered(self):

print("OTHER altered()")

class Child(object):

def __init__(self):

self.other=Other()

def implicit(self):

self.other.implicit()

def override(self):

print("CHILD override()")

def altered(self):

print("CHILD,BEFORE OTHER altered()")

self.other.altered()

print("CHILD,AFTER OTHER altered()")

son=Child()

son.implicit()

son.override()

son.altered()

‘‘‘

先来看看输出结果吧:

OTHER implicit()

CHILD override()

CHILD,BEFORE OTHER altered()

OTHER altered()

CHILD,AFTER OTHER altered()

开始的时候,你可能看不懂输出结果,我现在来给你整理一下思路。

很明显,类Other和类Child并没有父类子类的关系,而是一个平等的关系。

但是我们可以看到类Child的__init__函数中有类Other,这就是“A里有B”的关系。

这些过程就是一个引用的过程,比如son.implicit():

son是类Child的一个实例,然后调用son.other.implicit(),

在__init__中,son.other是等于Other(),所以调用的这一句是等同于Other().impicit(),

所以输出结果自然就成了类Other里的implicit()函数的打印信息。

这种方式就叫做合成,不需要继承关系,而是选择了“A里面有B”的引用方式,能有效避免多种继承。

‘‘‘

‘‘‘

⑥继承和合成的应用场合:

“继承和合成”的问题说到底还是为了解决关于代码复用的问题,什么时候用哪一种方式取决于你的需求。

继承可以让你在基类里隐含父类的功能,从而解决这个问题。

合成则是利用模块和别的类中的函数调用达到了相同的目的。

以下是一些老程序员的建议:

1.不惜一切代价地避免多重继承,因为它带来的麻烦比能解决的问题都多。如果非要用,

那得准备好专研类的层次结构,以及花时间去找各种东西的来龙去脉。

2.如果有一些代码会在不同位置和场合应用到,那就用合成来把它们做成模块。

3.只有在代码之间有清楚的关联,可以通过一个单独的共性联系起来的时候使用继承,

或者受现有代码或者别的不可抗拒因素所限非用不可,那也用吧。

‘‘‘

希望你已经理解了继承与合成,与此同时,我要恭喜你,你已经有了自食其力的能力了。

现在你需要制作一个游戏。在这儿,我有一些建议要告诉你:

①函数的风格:

学会合理使用继承与合成;

让函数保持简洁小巧;

不要让某一个函数做太多的事情。

②类的风格:

类的命名使用"驼峰式大小写",例如SuperMan,ComeOn等等;

__init__不应该做太多的事情;

类下面定义的函数名称不要用驼峰式大小写,用别的;

用一致的方式组织函数的参数,如果类处理users,dogs和cats,请在其它地方也保持这个次序。

③代码的风格:

合理使用空行;

每行不超过80个字符;

让自己的代码整体看起来简洁美观一点;

④好的注释:

养成注释的好习惯;

写注释的时候描述清楚为什么要这样做;

让注释尽量看起来短小精悍;

⑤客观评价你的代码;

主动反思代码有哪些不能实现的功能;

当你不能很好地实现可能存在的情况,请在注释里写出来。

祝你能顺利写出一个好游戏!

‘‘‘

①隐式继承:

当你在父类里定义了一个函数但没有在子类中定义的例子时,会发生隐式继承。

‘‘‘

# class Parent(object):

# def implicit(self):

# print("PARENT implicit()")

# class Child(Parent):

# pass #类Child里面没有定义任何细节,但是从Parent父类继承所有的行为。

# dad=Parent()

# son=Child()

# dad.implicit()

# son.implicit()

‘‘‘

我们可以看见,输出结果都是:PARENT implicit()。

说明了子类Child继承了父类Parent的所有功能,这就是隐式继承。

方法是创建一个子类的时候,括号内填入父类函数,不定义任何东西,就能完成继承父类的功能。

优点:需要很多类时,这样可以避免重复写很多代码。

‘‘‘

‘‘‘

②显式覆盖:

有时候,需要让子类的函数有一个不同的行为,显然这是隐式继承做不到的差事。

在子类中Child定义一个相同名称的函数就可以了。

‘‘‘

# class Parent(object):

# def override(self):

# print("PARENT override()")

# class Child(Parent):

# def override(self):

# print("CHILD pverride()")

# dad=Parent()

# son=Child()

# dad.override()

# son.override()

‘‘‘

从输出结果来看:

PARENT override()

CHILD pverride()

虽然子类Child继承了父类Parent的功能,但并不代表输出结果就应该一样。

dad.override()的打印结果应该没问题,这是Parent父类的一个实例。

son.override(),son是从子类Child的一个实例,虽然它继承了父类Parenet的功能,

但是类Child的实例是由类Child下定义的函数来执行,虽然函数名一样,但是函数内的变量属于局部变量大家应该知道。

当类Child下没有定义任何的函数时,这时候才去继承父类Parenet里面的函数来执行,这个过程就叫隐式继承。

大家弄清楚隐式继承和显式覆盖的区别了吗?

‘‘‘

‘‘‘

③在运行前或运行后替换:

这是使用继承的第三种方法。这种情况下,你想在父类中定义的内容运行之前或者之后在修改行为。

‘‘‘

# class Parent(object):

# def altered(self):

# print("PARENT altered()")

# class Child(Parent):

# def altered(self):

# print("CHILD,BEFORE PARENT altered()")

# super(Child,self).altered()

# print("CHILD,AFTER PARENT altered()")

# dad=Parent()

# son=Child()

# dad.altered()

# son.altered()

‘‘‘

我们先来看一下输出的结果:

PARENT altered()

CHILD,BEFORE PARENT altered()

PARENT altered()

CHILD,AFTER PARENT altered()

第一行、第二行和第四行的输出结果应该没有什么疑问,现在重点分析第三行的输出结果。

super(Child,self).altered(),这一行调用了父类的函数,我们先分析以下super()的作用:

super() 函数是用于调用父类(超类)的一个方法,这个方法是用来解决多重继承的问题,直接调用父类。

那么我们可以把这句话解读为:

用Child和self这两个参数调用super,找到Child的父类Parent,把子类Child实例的对象转换为父类Parent的实例对象,

然后在此返回的基础上调用altered函数。到这里Parent.altered就会被打印,从而打印出父类里的信息。

我这么解释是不是比较通俗易懂?希望对你有帮助!

‘‘‘

原文地址:https://www.cnblogs.com/Masterpaopao/p/10121665.html

时间: 2024-08-06 11:45:33

继承与合成的相关文章

面向对象【day07】:类的属性-继承-经典类

本节内容 类的公有属性 析构函数 类的继承 新式类和经典类 一.类的公有属性 一.概述 前面我们讲了类的私有属性,现在我们来说说类的公有属性,这边很容易被人弄混淆,有人觉的,在__init__()构造方法中,除了私有属性,其他的都是公有属性了,其实这是一个错误的结论,并不是定义在__init__()初始化方法中的属性是公有属性(除私有属性),那什么是公有属性呢?揭起了大家的好奇心. 定义:指的是所属这个类的所有对象,都可以访问的属性,叫做公有属性. 二.公有属性 2.1 定义 说明:在类中直接定

OOP三个基本特征:封装、继承、多态

面向对象的三个基本特征是:封装.继承.多态. 封装 封装最好理解了.封装是面向对象的特征之一,是对象和类概念的主要特性. 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏. 继承 面向对象编程 (OOP) 语言的一个主要功能就是“继承”.继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展. 继承现有类 + 扩展 通过继承创建的新类称为“子类”或“派生类”. 被继承的类称为“基类”.

17.Python自学之路:面对过程编程之继承

继承 面向对象编程 (OOP) 语言的一个主要功能就是"继承".继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展. 通过继承创建的新类称为"子类"或"派生类". 被继承的类称为"基类"."父类"或"超类". 继承的过程,就是从一般到特殊的过程. 抽象类仅定义将由子类创建的一般属性和方法. OO开发范式大致为:划分对象→抽象类→将类组织成

JAVA的三大特征 封装继承多态- 简单总结

简单总结一下 封装-即从很多类的抽取相同的代码 写在一个类里. 好处是 代码的重用,安全. 继承-减少代码的书写. 其好处也是 代码的重用. 多态- 把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化. 总的来说还是接口的重用. 那么总结一下,JAVA的三大特征 其好处 就是代码与接口的重用. 封装可以隐藏实现细节,使得代码模块化: 继承可以扩展已存在的代码模块(类): 它们的目的都是为了——代码重用. 而多态则是为了实现另一个目

黑马程序猿——JAVA面向对象的特性:封装,继承,多态

                                       - ----------android培训.java培训.java学习型技术博客.期待与您交流!------------  一.java面向对象的特性.封装.继承.多态         封装 封装是对象和类概念的主要特性. 封装.也就是把客观事物封装成抽象的类.而且类能够把自己的数据和方法仅仅让可信的类或者对象操作,对不可信的进行信息隐藏. 继承 面向对象编程 (OOP) 语言的一个主要功能就是"继承".继承

【python】-- 类的继承(新式类/经典类)、多态

继承 之前我们说到了类的公有属性和类的私有属性,其实就是类的封装,现在准备随笔的 是继承,是面向对象的第二大特性. 面向对象编程 (OOP) 语言的一个主要功能就是"继承".继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展. 通过继承创建的新类称为"子类"或"派生类",被继承的类称为"基类"."父类"或"超类",继承的过程,就是从一般到

合成/聚合复用原则,桥接模式

问题: 方式一, 方式二, 存在问题: 继承带来的麻烦,无论是哪种方式,一旦功能增多.品牌增多,增长不可控的无限变大.增加一个品牌,增加m个软件类+1个品牌类:增加一个软件,增加n(品牌个数)软件个类. 对象的继承关系在编译时就定义好了,所以无法在运行时改变从父类继承的实现. 子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化. 当需要复用子类时.如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换.这种依赖关系限制了灵活性,并最终限

设计模式之合成/聚合利用原则(CARP)

一.概念 CARP:CompositionAggregation Principle 合成聚合复用原则,尽量使用合成/聚合,尽量不使用类继承.合成聚合是"has  a"的关系,而继承是"is  a"的关系.由于继承是一中强耦合的结构,父类变,子类必变.所以不是"is  a"关系,我们一般不要用继承.优先使用合成聚合复用原则,有助于保持每个类的封装,降低继承的层次.合成/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分:

黑马程序员——JAVA面向对象的特性:封装,继承,多态

                                       - ----------android培训.java培训.java学习型技术博客.期待与您交流!------------  一.java面向对象的特性,封装.继承.多态         封装 封装是对象和类概念的主要特性. 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏. 继承 面向对象编程 (OOP) 语言的一个主要功能就是"继承".继承是