一、什么是面向对象技术中的继承机制?
1、面向对象技术中的继承机制的简介
(1)面向:即指向,aim at 的意思。
(2)对象:即object。可以是具体的人、事物,如那位警察叔叔,那一朵玫瑰花等,也可以是抽象意义的人、事物,如人(人的统称),花(各类花的统称)等。
(3)面向对象技术:,简称面向对象,指把事务分解成一个个对象,然后由对象之间分工与合作,是以对象功能来划分问题的一门技术。
(4)继承:即inheritance。比如:你继承你父亲的头脑发达的属性(特点),小猫继承了母猫的会捉老鼠的属性。但这里的继承还是可以覆盖的。
(5)机制:官方指各要素之间的结构关系和运行方式,是经过实践检验证明有效的、较为固定的处理方式或方法。
(6)面向对象技术中的继承机制:指面向对象软件技术中有"继承"这一个基本特点(除了继承,还有封装、多态),继承可以使得子类具有父类的属性和方法、重新定义方法、扩展或追加属性和方法等,继承是一种技术,它使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发成本。
比如:
鸟禽类有翅膀的属性、会吃东西的方法、会飞翔的方法;
老鹰继承鸟禽类有翅膀的属性、会吃东西的方法、会飞翔的方法;
鸵鸟继承鸟禽类有翅膀的属性、会吃东西的方法、会飞翔的方法;但鸵鸟继承鸟禽类会飞翔的方法,进行重写(基因的选择性表达)成平常只会飞奔地跑的方法。
2、面向对象的继承机制的作用
(1)面向对象的继承机制主要具有双重作用:一是作为类的构造机制,二是作为类型的构造机制。
作为类的构造机制,继承通过扩充、组合现有的类来构造新的类。扩充是指形成现有类的特例——派生类,组合是指抽取出若干现有类的共性形成新的抽象层次——基类。
作为类型的构造机制,如果子类继承父类,则所有要求对象为父类类型的地方也可以接受子类类型的对象。也就是说父类对象出现的地方可以用子类对象替代。
(2)总而言之,作为类的构造机制,继承是面向对象程序设计语言中支持软件重用的一种重要方式,而作为类型构造机制,继承是实现动态多态性的基础。
3、不同编程语言,继承机制也不太相同
(1)对于不同的编程语言,面向对象的继承机制有相同的地方,同样也有不同的地方。如:Java只提供单继承;C++、Python单继承和多继承都有提供(Python语言是由C及C++语言开发的);由于JavaScript是脚本语言,因而继承机制也有很多细节。这些语言下文会说到,其它语言就不多说了。
二、Java面向对象的继承机制
1、Java继承的浅析
(1)在日常生活中,我们经常用到“是一种(is-a)”关系来组织和表达知识,从而将知识组织成一种有层次、可分类的体系结构。例如,鸭梨是一种梨,梨是一种水果;大叶榕是一种树,树是一种植物等等,数不胜数。
(2)在Java面向对象程序中,用is-a关系来描述类与类之间的关系,称之为继承(inheritance)。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。继承是java程序设计中的一项核心技术。
(3.1)有时候,我们需要创建一个新的类A,但是它具有一个已存在的类B所具有的功能,同时也有类B没有的功能。那么,我们可以认为类A与类B存在is-a关系,“A is-a B”。可以将A比作树,将B比作植物,那么树具有植物的特性,同时也具有树特有的特性,以区别花、草等其他植物。
(3.2)创建类A时,我们可以将类B的代码复制过来,然后再在这个基础上添加代码。但这个方法让我们感到很麻烦。我们还可以用面向对象中的继承机制来构建这个新的类,我们只需要添加那些特有的特征就行了。使用继承机制,就不必每次都从头开始定义一个新的类,而是将这个新的类作为一个或者若干个现有的类的扩充或特殊化。
(4)在以上类A和类B的例子中,类A继承了类B,那么类B称为类A的父类(parent)、超类(super-class)或基类(base),而类A称为类B的子类(child)或派生类(derived-class)。一个类的祖先类(ancestor)包括了其父类及其父类的祖先类,一个类的后代类(descendant)包括了其子类及其子类的后代类。类A继承了类B也可以说是类B派生出了类A。
2、Java继承的语法
(1)java中用保留字extends表示继承关系。继承的语法格式为:
public class 子类 extends 父类 { //子类新增的数据成员和方法 //子类重写父类的成员和方法}
(2)下面举例来说明java的继承机制,以员工和经理的关系为例。
(2.1)代码清单1:Employee.java
import java.util.Date;import java.util.GregorianCalendar;// 员工类public class Employee { private String name; private double salary; private Date hireDay;/*** 构造函数、构造器* @param n 员工姓名* @param s 员工工资* @param year 雇佣年份* @param month 雇佣月份* @param day 雇佣日*/ public Employee(String n,double s,int year,int month,int day){ this.name=n; this.salary=s; GregorianCalendar calendar=new GregorianCalendar(year,month-1,day); this.hireDay=calendar.getTime(); }// 获取员工姓名 public String getName(){ return this.name; }// 获取员工工资 public double getSalary(){ return this.salary; }// 获取员工的雇佣日 public Date getHireDay(){ return this.hireDay; }// 提成工资 public void raiseSalary(double byPercent){ double raise=salary*byPercent/100; salary+=raise; }}
(2.2)代码清单2:Manager.java
// 经理类,继承了员工类Employee,经理也是员工。public class Manager extends Employee{ private double bonus; /** * 构造函数 * @param n 员工姓名 * @param s 员工工资 * @param year 雇佣年份 * @param month 雇佣月份 * @param day 雇佣日 */ public Manager(String name,double s,int year,int month,int day){ super(name, s, year, month, day); this.bonus=0; } // 经理的工资 = 基本工资 + 奖金 public double getSalary(){ double baseSalary=super.getSalary(); return baseSalary+bonus; } // 奖金 public void setBonus(double b){ bonus=b; }}
(2.3)代码清单3:ManagerTest.java
public class ManagerTest { public static void main(String[] args){ Manager boss=new Manager("Spring",80000,1987,12,15); boss.setBonus(5000); Employee[] staff=new Employee[3]; staff[0]=boss; staff[1]=new Employee("SpringMVC",50000,1989,10,1); staff[2]=new Employee("Mybatis",40000,1990,3,15); for(Employee e:staff) System.out.println("name:"+e.getName()+",salary="+e.getSalary()); }
运行结果:
name:Spring,salary=85000.0
name:SpringMVC,salary=50000.0
name:Mybatis,salary=40000.0
(3)在以上程序中,Employee类型的对象staff[0]引用了Manager类型的对象,程序仍能正常运行,说明了程序中任何出现超类的地方都可以用其子类置换。
(4)在此例中,staff[0]与boss引用同一个对象,但编译器仍认为staff[0]是Employee类型的对象,boss为Manager类型的对像,所以staff[0]不能用Manager类的方法。如staff[0].setbounds(5000);
是错误的用法,编译器会报错。
3、Java继承成员访问控制
(1)继承机制引入了受保护(protected)成员,提供了一种新的成员访问控制级别,其可以理解为介于公有(public)和私有(private)之间。
(2)在继承中,子类继承了超类除构造函数之外的所有成员,这些成员成为子类的继承成员。继承成员不仅包括超类中定义的共有、受保护及私有成员,还包括了超类的继承成员。
(3)在子类中,子类可以访问自身定义的所有成员,也可以访问父超类的共有和受保护继承成员,但不能访问超类的私有继承成员。
(4)继承成员在子类中的访问控制与它们在超类中的访问控制相同。即【1】原先在超类中是共有的成员,被子类继承后认为共有的成员;【2】原先在超类中是受保护的成员,被子类继承后仍为受保护的成员;【3】原先在超类中是私有的成员,被子类继承后认为私有的成员,但子类不能访问。数据类型为子类的对象不能访问子类的及其父类的受保护成员。
4、重定义
(1)当子类要用到超类中的一个方法,而超类的这个方法有不适合子类时,我们可以选择在子类中重定义这个方法。子类重定义超类的方法是指的方法是指在子类中定义一个与超类的某个方法有完全相同接口的方法,这是称子类的这个方法重定义了超类同接口的方法。所为方法接口完全相同是指返回类型、方法名和方法参数列表完全相同。比如:上例子中,Manager类的getSalary()方法就是重定义了超类Employee的getSalary()方法。
(2)在重定义超类(祖先类)的方法时,不允许降低方法的访问控制权限。如在超类中是public的方法,在子类重定义时不能定义成protected或private。不过java允许在子类中重定义成员数据是降低其访问控制权限。如在超类中是public的数据成员,在子类可重定义为protected或private的数据成员。
(3)对于成员数据来说,只要子类定义了与祖先类同名的成员数据就是重定义了祖先类的成员数据,而且屏蔽了祖先类的成员数据,子类及子类的使用者再也不能访问该祖先类的成员数据。
5、this与super
(1)java中的this与super都是每一个对象实例中的特殊私有成员,是一个引用变量,this的类型是该对象实例所属的类类型,super的类型是它所属的对象实例的超类类型。
(2)它们有两种使用方式:
(2.1)第一种:调用类的数据或方法成员。
this是调用自身的成员,而super则是调用其超类(祖先类)中的数据成员。如以上例子的Employee类,其部分代码如下:
public class Employee { private String name; // ...... public Employee(String name,double s,int year,int month,int day){ this.name=name; // ......... } // .......}
在构造函数中用到了this.name=name;
,this指明了name是它所在的类(Employee)中定义的name,而不是参数列表中的那个name。这种使用方法的格式总结为:this.本类成员
和 super.超类成员
。其中超类成员不能是私有成员。
(2.2)第二种:调用构造函数。
this是在自身的构造函数中调用自身的另一个构造函数,而super则是在自身的构造函数中调用其超类的构造函数。例如Manager类的构造函数:
public Manager(String name,double s,int year,int month,int day){ super(name, s, year, month, day);//必须放在第一句 this.bonus=0;}
其中有super(name, s, year, month, day);
,这是Manager的构造函数调用了其超类Employee的构造函数,初始化了继承成员name、salary、hireDay。这种使用方法的格式总结为:this(参数列表);
和 super(参数列表);
。注意,他们都必须放在构造方法的第一句。
6、小结
子类拥有父类非private的属性,方法。
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法(重定义)。
java的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如A类继承B类,B类继承C类,所以按照关系就是C类是B类的父类,B类是A类的父类,这是java继承区别于C++继承的一个特性。但接口允许多继承。
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
三、JavaScript面向对象的继承机制
1、JavaScript面向对象的继承机制的前言
(1)在JavaScript面向对象的继承之前,我们要补充几点关于创建对象的知识。创建对象有好几种方法,其中最常用的方法是混杂模式创建对象,我们说到,混杂模式创建对象有一个缺点是构造函数和原型方式是分开的,这一点可能会显得不能够体现封装的概念。那么,可以考虑如何把构造函数和原型放在一起封装起来呢?请看如下代码:
function Person(name,age) { this.name=name; this.age=age; if(typeof this.sayname !="function") { Person.prototype.sayname=function() { //sayname方法不存在才执行 alert(this.name); } }}
(2)以上代码其实就是动态原型模式创建对象的方法,动态原型模式有一个缺点,无法使用对象字面量重写原型,因为重写原型就会切断现有实例和新原型直接的联系,即constructor属性指向了object。
2、原型链继承
(1)在说javascript如何实现继承机制的之前,先说一下继承? 所谓继承,就是指一个对象拥有另一个对象的方法和属性。javascript中最重要的概念之一原型,本质上就是所有函数都有一个prototype属性,指向一个所有实例共享的原型对象。原型链继承的机制也是利用这一点来实现的。
(2)如果我们将子类型的原型对象等于父类型的实例,就相当于将子类型的原型对象指向了父类型的原型对象,并且子类型也得到了父类型的实例上的所有属性和方法,这样就构成了一个所谓的原型链,本质上就是各类型的原型链接起来了,这样子类型就继承了父类型的属性和方法,模拟出继承的机制。换句话说,原型链继承就是重写了子类型的原型对象,将父类型的实例属性和方法继承到子类型的原型对象中,并且与父类型的原型对象链接起来了。
(3)请看以下图1-原型链继承流程图(引用图片来自李炎恢教程PDF):
(4)通过以上示例图我们可以看到,table的原型对象链接到了desk,desk又链接到了box,box实际上链接到了object,这一步是javascript自动帮我们完成的。
(5)那么,代码如何写呢?请看以下代码(省略了Table继承,此代码引用了李炎恢教程的PDF):
function Box(){this.name="sun";}function Desk(){this.age="10";}Desk.prototype=new Box();//子类型的原型等于父类型的实例,形成原型链var desk=new Desk();alert(desk.age);//10
(6)了解了原型链的继承,那么我们会发现原型链的继承有一些缺点,和原型创建对象一样,原型链继承也会导致一些不该被共享的属性共享了,使得某些实例修改了该属性导致全部实例也修改了该属性。例如以下代码:
function SuperType(){ this.colors=["red","blue","green"]; }function SubType(){}//原型链继承SubType.prototype=new SuperType(); var instance1=new SubType();instance1.colors.push("black");alert(instance1.colors);//red blue green blackvar instance2=new SubType();alert(instance2.colors);//red blue green black
(7)可以看到,instance2和instance1都有同样的colors属性。如何解决呢?正如上一节讲过的,我们可以利用在构造函数里定义这些不需要被共享的属性。那么,如何利用构造函数实现继承呢?基本思想是在子类型的构造函数中调用父类型的构造函数,具体请看以下代码:
function SuperType(name){ this.name=name; this.colors=["red","blue","green"]; }function SubType(name,age){ SuperType.call(this,name); //继承了SuperType,这叫做对象冒充 this.age=age}var instance1=new SubType("ying","24");instance1.colors.push("black");//alert(instance1.colors);var instance2=new SubType("jian","23");alert(instance2.colors);
(8)以上代码中使用了call()方法,实现了继承。call()方法接受2个参数,第一个参数是运行函数的作用域,第二个参数是运行函数的参数。所谓运行函数就是调用此call()方法的函数,call()方法是函数内部的方法。例如在上面代码中,call()方法的运行函数就是SuperType构造函数,this指的是当前对象。利用构造函数实现继承也有一个问题就是方法不能复用,那么很明显我们可以将原型链和构造函数的继承方法组合起来实现组合继承。
请看以下代码:
function SuperType(name) { this.name=name; this.colors=["red","blue","green"];}SuperType.prototype={ constructor:SuperType, sayname:function(){ alert(this.name); }};function SubType(name,age) { SuperType.call(this,name); //在子类型内部也调用了父类型的构造函数 this.age=age;}SubType.prototype=new SuperType(); //创建子类型原型调用了父类型的构造函数SubType.prototype.sayage=function() { alert(this.age);} var instance1=new SubType("sun","24");var instance1=new SubType("liang","23");
(9)组合继承的缺点是无论在什么情况下,都会调用两次超类型构造函数,一次是在创建子类型原型的时候,另一次是在子类型构造函数内部,这样导致的结果就是有两组name和colors属性,一组在实例上,另一组在SubType原型中。
3、原型式继承
(1)基本思想:借助原型可以基于已有的对象创建新对象。请看以下函数代码:
function objectClone(object) { function F(){} F.prototype=object; return new F();}
(2)这段代码的意思是在函数内部先创建一个临时的构造函数F(),然后将F的原型继承已有的对象object,最好返回F对象的一个实例,这样的结果就是创建了一个继承了已有对象object的新对象。然后再以某种方式加强新对象,实现其他功能。例如以下代码:
function objectClone(object){ function F(){} F.prototype=object; return new F();}var person={ name:"sun", friends:["tian","wang"]};var anotherperson=objectClone(person);anotherperson.name="fang";anotherperson.friends.push("liu");
(3)ES5 新增了一个Object.creat()方法规范了原型式继承。这个方法接收2个参数,一个用作新对象原型的对象,一个为新对象定义额外属性的对象。在没有必要兴师动众的创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承完全可以胜任。但是不要忘记了,包含引用类型值的属性始终都会共享相应的值。
4、寄生式继承
(1)寄生式继承的思路与工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回这个对象,以下面的例子,基于person对象返回了一个新对象anotherperson,它不仅具有person的所有属性和方法,也有自己的sayhi方法。在主要考虑对象不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。
先看以下代码:
function createObject(origianl){ var clone=objectClone(original); clone.sayHi=function(){ alert("Hi"); }; return clone; }var person={ name:"sun", friends:["tian","wang"]};var anotherperson=creatObject(person);anotherperson.sayHi();
5、寄生组合式继承
(1)根据前面,组合继承的缺点是无论什么时候都要调用2次父类型的构造函数,子类型最终会包含父类型对象的全部实例属性,但我们不得不在调用子类型构造函数的时候重写这些属性。因此,我们可以这样想,不必为了指定子类型的原型而调用父类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。
请看以下代码,实现了上面的想法:
function superClone(subType,superType){ //创建一个superType对象的副本,即创建继承了superType的新对象 var clone=objectClone(superType); //增强对象 clone.constructor=subType; //指定对象 subType.prototype=clone; }
(2)本质上来说就是,使用寄生式继承来继承父类型的原型,然后将结果指定给子类型的原型。
(3)完整的寄生组合式继承的代码如下:
function objectClone(object){ function F(){} F.prototype=object; return new F();}function superClone(subType,superType){ var clone=objectClone(superType); clone.constructor=subType; subType.prototype=clone;}function SuperType(name){ this.name=name; this.friends=["ye","wang","xu"];}SuperType.prototype.sayname=function(){ alert(this.name);}function SubType(name,age){ SuperType.call(this,name); this.age=age;}superClone(SubType,SuperType);SubType.prototype.sayage=function(){ alert(this.age);}var instance1=new SubType("sun","24");instance1.friends.push("fang");alert(instance1.friends); //ye wang xu fangvar instance2=new SubType("liu","25");alert(instance2.friends); //ye wang xu
(4)这个例子的高效率体现在它只调用了一次SuperType()构造函数,并且因此避免了在SubType.prototype上面创建不必要的多余的属性,与此同时,原型链还能保持不变。所以,寄生组合式继承是最理想的继承模式。
6、JavaScrpit继承小结:
(1)构造函数里存放的是那些特有属性和方法,每个对象都应该不同的。而原型对象里存放的应该是一些共享的属性和方法,每个对象都可以复用。
(2)继承机制有很多种,但是本质都一样。那就是子类型都要拥有父类型的所有属性和方法,这样的过程就是继承。
(3)原型链继承机制就是利用子类型的原型对象等于父类型的实例来实现的,这个过程实际上就是重写子类型的原型对象,在子类型的原型对象中拥有父类型的构造函数里的所有属性和方法,并且将父类型的原型对象链接起来了。
(4)构造函数继承机制就是利用call()函数在子类型的构造函数中调用父类型的构造函数,实现继承。
(5)组合继承机制就是综合以上的原型链机制和构造函数机制,原型链实现原型属性和方法的继承,构造函数实现对实例属性和方法的继承。
(6)原型式继承机制就是创建一个已有对象的副本。
(7)寄生式继承和原型式继承本质上是一样的,只不过它在创建对象副本以后使用同一的方法增强对象。
(8)寄生组合继承的机制就是构造函数继承和寄生式继承的组合,其中这里的寄生式继承就是创建父类型的原型对象副本,然后把子类型的原型对象链接上去,这样子类型原型对象上就不会有父类型的实例属性和方法,避免了在子类型的原型对象上创建多余的不必要的属性。
四、Python面向对象的继承机制
1、单继承
1.1、单继承的简介
(1)继承的概念:子类拥有父类的所有方法和属性。
(2)继承的语法:
class 类名(父类名): pass
(3)子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发。
(4)子类中应该根据具体情况,封装子类特有的属性和方法。
(5)继承的传递性:C 类从 B 类继承,B 类又从 A 类继承。那么 C 类就具有 B 类和 A 类的所有属性和方法。子类拥有父类以及父类的父类 中封装的所有属性和方法。
(6)俗话举例:
Cat 类是 Animal 类的子类,Animal 类是 Cat 类的父类,Cat 类从 Animal 类继承。
Cat 类是 Animal 类的派生类,Animal 类是 Cat 类的基类,Cat 类从 Animal 类派生。
(7)动物的继承图示化:
1.2、方法的重写
(1)子类拥有父类的所有方法和属性。子类继承自父类,可以直接享有父类中已经封装好的方法,不需要再次开发。当父类的方法实现不能满足子类需求时,可以对方法进行重写(override)。
(2)重写父类方法有两种情况:
(2.1)覆盖父类的方法:如果在开发中,父类的方法实现和子类的方法实现,完全不同时,就可以使用覆盖的方式,在子类中重新编写父类的方法实现。具体的实现方式,就相当于在子类中定义了一个 和父类同名的方法并且实现。重写之后,在运行时,只会调用子类中重写的方法,而不再会调用父类封装的方法。
(2.2)对父类方法进行扩展:如果在开发中,子类的方法实现中包含父类的方法实现,父类原本封装的方法实现是子类方法的一部分时,就可以使用扩展的方式。在子类中重写父类的方法时,在需要的位置使用 super().父类方法来调用父类方法的执行代码,其他的位置针对子类的需求,编写子类特有的代码实现。
(3)关于 super
(3.1)在 Python 中 super 是一个特殊的类,super() 就是使用 super 类创建出来的对象。最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现。在 Python 2.x 时,如果需要调用父类的方法,还可以使用以下方式:
父类名.方法(self)
(3.2)这种方式,目前在 Python 3.x 还支持这种方式。但这种方法不推荐使用,因为一旦父类发生变化,方法调用位置的类名同样需要修改。在使用时,父类名 和 super() 两种方式不要混用。如果使用当前子类名调用方法,会形成递归调用,出现死循环。
1.3、父类的私有属性和私有方法
(1)简介:
子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法。
子类对象可以通过父类的公有方法间接访问到私有属性或私有方法。
私有属性、方法是对象的隐私,不对外公开,外界以及子类都不能直接访问。
私有属性、方法通常用于做一些内部的事情。
(2)示例:
B 的对象不能直接访问 num2 属性。
B 的对象不能在 demo 方法内访问 num2 属性。
B 的对象可以在 demo 方法内,调用父类的 test 方法。
父类的 test 方法内部,能够访问 num2 属性和 test 方法。
2、多继承
2.1、多继承的简介
(1)子类可以拥有多个父类,并且具有所有父类的属性和方法。例如:孩子会继承自己父亲和母亲的特性。
(2)语法:
class 子类名(父类名1, 父类名2...) pass
(3)如果不同的父类中存在同名的方法,子类对象在调用方法时,会调用哪一个父类中的方法呢?答:(4)或(5)
如果父类之间存在同名的属性或者方法,应该尽量避免使用多继承。
(4)如果父类之间存在同名的属性或者方法,应该尽量避免使用多继承。
(5)Python 中针对类提供了一个内置属性 mro 可以查看方法搜索顺序。MRO是 method resolution order,主要用于在多继承时判断方法、属性的调用路径。
(5.1)如图4-多继承图所示:
(5.2)执行如下代码:
print(C.__mro__)
(5.3)输出结果:
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
(5.4)在搜索方法时,是按照 mro 的输出结果 从左至右的顺序查找的。如果在当前类中找到方法,就直接执行,不再搜索。如果没有找到,就查找下一个类中是否有对应的方法,如果找到,就直接执行,不再搜索。如果找到最后一个类,还没有找到方法,程序报错。
2.2、新式类与旧式(经典)类
object 是 Python 为所有对象提供的基类,提供有一些内置的属性和方法,可以使用 dir 函数查看。
新式类:以 object 为基类的类,推荐使用。经典类:不以 object 为基类的类,不推荐使用。
在 Python 3.x 中定义类时,如果没有指定父类,会默认使用 object 作为该类的 基类 —— Python 3.x 中定义的类都是新式类。
在 Python 2.x 中定义类时,如果没有指定父类,则不会以 object 作为 基类。
新式类 和 经典类 在多继承时 —— 会影响到方法的搜索顺序。
为了保证编写的代码能够同时在 Python 2.x 和 Python 3.x 运行,今后在定义类时,如果没有父类,建议统一继承自 object。
参考文献
[1] JavaScript 高级程序设计 第4版,马特·弗里斯(Nicholas C.Zakas),人民邮电出版社
[2] Python学习手册(原书第5版),[美]马克·卢茨(Mark Lutz),机械工业出版社
[3] 马士兵(马老师)Java教程
[4] 李炎恢教程PDF
[5] Python教程文档:https://blog.csdn.net/adrrry
[6] 兄弟连教育:www.itxdl.cn
[7] 菜鸟教程:www.runoob.com
[8] JavaScript:www.javascript.com