第十章 面向对象之继承与派生

继承与派生

继承

  当我们创建一个类时,新建的类可以继承一个或多个父类(python支持多继承),父类又可以称为基类或超类,新建的类称为派生类或子类,子类会继承父类的属性,可以减少代码冗余

#单继承和多继承
class ParentClass1:
    pass

class ParentClass2:
    pass

class subClass1(ParentClass1):  #单继承
    pass

class subClass2(ParentClass1,ParentClass2): #多继承,用逗号分隔开多个继承的类
    pass

#查看继承的父类
print(subClass1.__bases__)
print(subClass2.__base__)   #只查看从左到右继承的第一个子类
print(subClass2.__bases__)  #查看所有继承的父类
#经典类和新式类
print(ParentClass1.__bases__)
#object类:所有python类的基类,提供了一些常见方法的实现(如__str__)
#经典类:没有继承object的类,以及该类的子类
#新式类:无论是否继承object,都默认继承object的类,以及该类的子类
#在python2中,类分为经典类和新式类;在python3中,所有类均为新式类
经典类和新式类

  继承描述的是子类与父类之间的关系,是一种“什么是什么”(例如:人是动物)的关系。要找出这种关系,必须先抽象再继承。

  抽象只是分析和设计过程中,一个动作或者一种技巧,通过抽象可以得到类

class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

class Student(People):
    def learn(self):
        print('%s is learning'%self.name)

class Teacher(People):
    def teach(self):
        print('%s is teaching'%self.name)

stu1=Student('lary',18)
teacher1=Teacher('egon',20)
stu1.learn()
teacher1.teach()

  属性查找顺序:对象->类->父类->父类的父类。。。

class Foo:
    def f1(self):
        print('foo f1...')

    def f2(self):
        print('foo f2...')
        self.f1()


class Bar(Foo):
    def f1(self):
        print('bar f1...')

b=Bar()
b.f2()
View Code

派生

   派生:子类可以添加自己的新属性,或者重新定义已经在父类中定义过的属性,一旦定义了与父类重名的属性,那么调用该属性时,以子类自己的属性为准

class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def tell_info(self):
        print('name:%s--age:%s'%(self.name,self.age))

class Student(People):
    def learn(self):
        print('%s is learning'%self.name)

    def tell_info(self):
        print('student:',end='')
        print('name:%s--age:%s' % (self.name, self.age))

stu1=Student('lary',18)
stu1.tell_info()

  在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即使是self参数也要为其传值

class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

class Student(People):
    def __init__(self,name,age,grade):
        People.__init__(self,name,age) #调用父类功能
        self.grade=grade                #新属性

组合

  组合:指的是在一个类中以另外一个类的对象作为数据属性

  组合与继承:通过继承建立了派生类与基类之间的关系,它是一种“是”的关系,当类之间有很多相同的功能,提取这些共同的功能做成基类;组合的方式建立了类与组合的类之间的关系,它是一种“有”的关系,当类之间有明显的不同,并且较小的类是较大的类所需要的组件时,用组合比较好

class People:
    def __init__(self,name,age,date_obj):
        self.name=name
        self.age=age
        self.birth=date_obj

    def tell_info(self):
        print('name:%s--age:%s'%(self.name,self.age))

class Student(People):
    def learn(self):
        print('%s is learning'%self.name)

    def tell_info(self):
        print('student:',end='')
        People.tell_info(self)

class Date:
    def __init__(self,year,mon,date):
        self.year=year
        self.mon=mon
        self.date=date

    def tell_birth(self):
        print('birth day is <%s-%s-%s>'%(self.year,self.mon,self.date))


day1=Date(1990,12,12)
#day1.tell_birth()
stu1=Student('lary',18,day1)
stu1.birth.tell_birth()

  抽象类:抽象类是一个特殊的类,特殊之处在于只能被继承,不能被实例化。抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法

#抽象类
import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod     #定义抽象方法,无需实现功能
    def eat(self):
        pass

class People(Animal):       #子类继承抽象类,但是必须定义抽象类中定义的方法(如eat方法)
    def eat(self):
        print('people is eating')

peo1=People()
peo1.eat()
抽象类

继承的实现原理

  继承顺序:如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性,如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先

 

class A(object):
    def test(self):
        print('from A')

class B(A):
    def test(self):
        print('from B')

class C(A):
    def test(self):
        print('from C')

class D(B):
    def test(self):
        print('from D')

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    # def test(self):
    #     print('from F')
    pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类
继承顺序

  继承原理:对于定义的每一个类,python会计算出一个方法解析顺序列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配的属性为止

  子类中调用父类的方法

# 子类重用父类的两种方法
# 父类名.父类方法()
class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def tell_info(self):
        print('name:%s---age:%s'%(self.name,self.age))

class Student(People):
    def __init__(self,name,age,sex):
        People.__init__(self,name,age)
        self.sex=sex

stu1=Student('lary',18,'female')
stu1.tell_info()

#super()
class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def tell_info(self):
        print('name:%s---age:%s'%(self.name,self.age))

class Student(People):
    def __init__(self,name,age,sex):
        super().__init__(name,age)  #参照MRO列表检查父类,获取属性
        self.sex=sex

stu1=Student('lary',18,'female')
stu1.tell_info()
重用父类方法的两种方式

多态与多态性

  多态指的是同一类事物有多种形态

#多态
class Animal:       #同一类事物:Animal
    pass

class Pig(Animal):  #Animal的形态之一:Pig
    def eat(self):
        print('Pig is eating')

class Dog(Animal):  #Animal的形态之二:Dog
    def eat(self):
        print('Dog is eating')

#多态性:在不考虑对象具体类型的情况下,直接使用对象
pig1=Pig()
dog1=Dog()
pig1.eat()
dog1.eat()
#多态性的好处:增加了程序的灵活性和扩展性
多态性

  鸭子类型:如果看起来像、叫声像而且走起路来像鸭子那么它就是鸭子

class Pig:
    def eat(self):
        print('Pig is eating')

class Dog:
    def eat(self):
        print('Dog is eating')

class Radio:
    def eat(self):
        print('Radio is eating')
View Code

  多态性的好处

s=str('hello')
l=list([1,2,3])
t=tuple((4,5,6))

print(len(s))
print(len(l))
print(len(t))
View Code

封装

  属性隐藏

#在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
#__开头的属性只是语法意义的变形,这种变形只在定义时发生一次,这种隐藏对外不对内
class Foo:
    __N=1            #将类的数据属性设置成私有的__N,在定义阶段会自动变形为_Foo__N

    def __init__(self,x,y):
        self.x=x
        self.__y=y

    def f1(self):
        print('f1')

    def f2(self):     #在内部可以访问私有的数据
        print("N:%s---Y:%s"%(self.__N,self.__y))

print(Foo.__dict__)
obj=Foo(1,2)
print(obj.f2())
print(obj.__dict__)
obj.__z=5
print(obj.__dict__)
print(obj.__z)      #变形的过程只在类的定义阶段发生,在定义后的赋值操作不会变形

#在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有
class Parent:
    def __f1(self):
        print('P f1')

    def f2(self):
        print('p f2')
        self.__f1()  #只会与自己所在的类为准,即调用_Parent__f2

class sub(Parent):
    def __f1(self):
        print('s f1')

s=sub()
s.f2()
View Code

  封装的意义:

  封装数据属性的意义:将数据隐藏起来然后对外提供操作该数据的接口,在该接口上对该数据操作进行限制,以完成对数据属性操作的严格控制

#封装数据属性的意义
class People:
    def __init__(self,name,age):
        self.set_info(name,age)

    def tell_info(self):
        print("name:%s---age:%s"%(self.__name,self.__age))

    def set_info(self,name,age):
        if type(name) is not str:
            raise TypeError('name must be str')
        self.__name=name
        self.__age=age

p=People('lary',18)
p.tell_info()
View Code

  封装方法的意义:隔离复杂度(对于使用者来说只需要知道接口的功能,其余功能可以隐藏起来,这样既隔离了复杂度,也提升了安全性)

  特性(property):property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值,这种特性的使用方式遵循了统一访问的原则

#特性(property)
class People:
    def __init__(self,name,age,height,weight):
        self.name=name
        self.age=age
        self.height=height
        self.weight=weight

    @property
    def bmi(self):
        return self.weight/(self.height ** 2)

egon=People('egon',18,1.80,75)
print(egon.bmi)
class People:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self,obj):
        self.__name=obj

    @name.deleter
    def name(self):
        #del self.__name
        raise PermissionError ('no del')

egon=People('egon',28)
print(egon.name)
egon.name='EGON'
print(egon.name)
del egon.name
View Code
posted @ 2018-01-25 17:16  日新其德止于至善  阅读(191)  评论(0编辑  收藏  举报