一个类被定义后,目标就是要把它当成一个模块来使用,并把这些对象嵌入到你的代码中去,#同其它数据类型及逻辑执行流混合使用。有两种方法可以在你的代码中利用类。第一种是组合#(composition)。就是让不同的类混合并加入到其它类中,来增加功能和代码重用性。你可以在一个#大点的类中创建你自已的类的实例,实现一些其它属性和方法来增强对原来的类对象。另一种方法#是通过派生.#class NewAddrBookEntry(object): # class definition 类定义# 'new address book entry class'# def __init__(self, nm, ph): # define constructor 定义构造器# self.name = Name(nm) # create Name instance 创建Name 实例# self.phone = Phone(ph) # create Phone instance 创建Phone 实例# print('Created instance for:', self.name)##NewAddrBookEntry 类由它自身和其它类组合而成。#*********** Part 9: 子类和派生 ***********************#当类之间有显著的不同,并且(较小的类)是较大的类所需要的组件时,组合表现得很好,但当#你设计“相同的类但有一些不同的功能”时,派生就是一个更加合理的选择了。#OOP 的更强大方面之一是能够使用一个已经定义好的类,扩展它或者对其进行修改,而不会影响#系统中使用现存类的其它代码片段。OOD 允许类特征在子孙类或子类中进行继承。这些子类从基类(或#称祖先类,超类)继承它们的核心属性。而且,这些派生可能会扩展到多代。在一个层次的派生关#系中的相关类(或者是在类树图中垂直相邻)是父类和子类关系。从同一个父类派生出来的这些类#(或者是在类树图中水平相邻)是同胞关系。父类和所有高层类都被认为是祖先。#####创建子类#创建子类的语法看起来与普通(新式)类没有区别,一个类名,后跟一个或多个需要从其中派生的父类:#class SubClassName (ParentClass1[, ParentClass2, ...]):# 'optional class documentation string'# class_suite#如果你的类没有从任何祖先类派生,可以使用object 作为父类的名字。##经典类的声明唯一不同之处在于其没有从祖先类派生---此时,
没有圆括号:#class ClassicClassWithoutSuperclasses:# pass#class Parent(object): # define parent class 定义父类# def parentMethod(self):# print('calling parent method')##class Child(Parent): # define child class 定义子类# def childMethod(self):# print('calling child method')# #p = Parent() # instance of parent 父类的实例#p.parentMethod()#-->#calling parent method##c = Child() # instance of child 子类的实例#c.childMethod() # child calls its method 子类调用它的方法#c.parentMethod() # calls parent's method 调用父类的方法##-->#calling child method#calling parent method
继承描述了基类的属性如何“遗传”给派生类。一个子类可以继承它的基类的任何属性,不管是数据属性还是方法。#class Parent(object): # define parent class 定义父类# def parentMethod(self):# print('calling parent method')##class Child(Parent): # define child class 定义子类# def childMethod(self):# print('calling child method')##c = Child() # instantiate child 实例化子类#print(c.__class__) # child "is a" parent 子类“是一个”父类#-->#print(Child.__bases__) # child's parent class(es) 子类的父类#-->(,)## 10.1 __bases__类属性#对任何(子)类,它是一个包含其父类(parent)的集合的元组。注意,我们明确指出“父类”是相对所有基类(它包括了所有祖先类)#而言的。那些没有父类的类,它们的__bases__属性为空。#class A(object): pass # define class A 定义类A#class B(A): pass # subclass of A A 的子类#class C(B): pass # subclass of B (and indirectly, A) B 的子类(A 的间接子类)#class D(B,A): pass # subclass of A and B A,B 的子类, 注意,这里B 要写在前面##print(A.__bases__)#print(C.__bases__)#print(D.__bases__)#-->#(,)#(,)#(, )## 10.2 通过继承覆盖(Overriding)方法#创建父类#class P(object):# def foo(self):# print('Hi, I am P-foo()') #p = P()#p.foo()#-->Hi, I am P-foo()#创建子类C,从父类P 派生:#class C(P):# def foo(self):# print('Hi, I am C-foo()')#c = C()#c.foo()#-->Hi, I am C-foo()#尽管C 继承了P 的foo()方法,但因为C 定义了它自已的foo()方法,所以 P 中的 foo() 方法#被覆盖。覆盖方法的原因之一是,你的子类可能需要这个方法具有特定或不同的功能.##如果想继续调用那个被我覆盖的基类方法,就需要去调用一个未绑定的基类方法,明确给出子类的实例.#P.foo(c)#-->Hi, I am P-foo()#可以在子类的重写方法里显式地调用基类方法:#class C(P):# def foo(self):# P.foo(self)# print('Hi, I am C-foo()')#注意,在这个(未绑定)方法调
用中我们显式地传递了self. 一个更好的办法是使用super()内建方法:#class C(P):# def foo(self):# super(C, self).foo()# print('Hi, I am C-foo()')#super()不但能找到基类方法,而且还为我们传进self,这样我们就不需要做这些事了。现在我#们只要调用子类的方法,它会帮你完成一切:#c = C()#c.foo()#-->#Hi, I am P-foo()#Hi, I am C-foo()#核心笔记:重写__init__不会自动调用基类的__init__#类似于上面的覆盖非特殊方法,当从一个带构造器 __init()__的类派生,如果你不去覆盖#__init__(),它将会被继承并自动调用。但如果你在子类中覆盖了__init__(),子类被实例化时,#基类的__init__()就不会被自动调用.#class P(object):# def __init__(self):# print "calling P's constructor"##class C(P):# def __init__(self):# print "calling C's constructor"##>>> c = C()#calling C's constructor#如果你还想调用基类的 __init__(),你需要像上边我们刚说的那样,明确指出,使用一个子#类的实例去调用基类(未绑定)方法。相应地更新类C,会出现下面预期的执行结果:#class C(P):# def __init__(self):# P.__init__(self)# print "calling C's constructor"#>>> c = C()#calling P's constructor#calling C's constructor##Python 使用基类名来调用类方法,对应在JAVA 中,是用关键字super 来实现的,这就是super()#内建函数引入到Python 中的原因,这样你就可以“依葫芦画瓢”了:#class C(P):# def __init__(self):# super(C, self).__init__()# print "calling C's constructor"#使用super()的漂亮之处在于,你不需要明确给出任何基类名字...“跑腿事儿”,它帮你干了!#使用super()的重点,是你不需要明确提供父类。这意味着如果你改变了类继承关系,你只需要改一#行代码(class 语句本身)而不必在大量代码中去查找所有被修改的那个类的名字