面向对象(二)--继承与派生、组合

一、什么是继承

继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。

二、继承的特点

1.继承描述的是事物的遗传关系,子类可以重用父类的属性,需要注意的是:继承是类与类之间的关系!!好处就是可以减少类之间的代码冗余问题

2.在Python中支持一个子类继承多个父类

3.Python类分为两种:新式类、经典类

  (1)新式类: 但凡继承了object的类Foo,以及该类的子类...都是新式类

    在python3中一个类即便是没有显式地继承任何类,默认就会继承object,即python3中所有的类都是新式类

  (2)经典类:没有继承object的类,以及该类的子类...都是经典类

    在python2中才区分新式类与经典类,在python2中一个类如果没有显式地继承任何类,也不会继承object

三、单继承和多继承

class Parent1():
    a = 3

class Parent2():
   a = 3

class Sub1(Parent1):         # 单继承,父类是Parent1,子类是Sub1
    a = 2
    pass


class Sub2(Parent1,Parent2):       # 多继承,父类是Parent1,Parent2,子类是Sub1
    a = 2
    pass

四、查看继承

利用__bases__方法可以查看子类的父类,只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类

class Parent1():
    pass

class Sub1(Parent1):
    pass

print(Sub1.__bases__)

五、继承的实现原理

C3算法与方法解析顺序(MRO)列表

属性查找的原则:

  1. 子类先于父类被查找
  2. 有多个父类时,会根据它们在列表中的顺序被查找
  3. 如果对下一个类存在两个合法的选择,则选择第一个父类

mro列表只适用于新式类,经典类不存在mro列表

只有在python2中才有经典类与新式类的区分,python3中全部是新式类

六、子类重用父类中的方法(派生)

1、派生:在子类中定义自己的属性,如果与父类的属性重名,那以自己的为准

2、在子类派生出的新方法中重用父类功能的方式一:指名道姓地访问父类中的函数

  Foo.__init__(self,name,age)     不能自动传值,有几个参数就传几个

(1)该方式与继承无关

(2)没有自动传值的效果

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


class Student(People):
    def __init__(self, name, age, sex, score=0):
        # 方式一 调用父类中的__init__函数
        People.__init__(self, name, age, sex)
        self.score = score

    def choose(self):
        print('%s choosing course' % self)


class Teacher(People):
    def score(self, stu, score):
        stu.score = score


stu = Student('张三', 12, 'male')
print(stu.__dict__)      # __dict__可以查看对象的名称空间中的名字
方式一

3、在子类派生出的新方法中重用父类功能的方式二:super(),只能在子类中用

  super().__init__(name,age)    自动传值

(1)在python2中:super(自己的类名,自己的对象)

  在python3:super()    

  调用super()会得到一个特殊的对象,该特殊的对象是专门用来引用父类中的属性的,!!!完全参照mro列表!!!

(2)super()注意点:

  a. 该方式与继承严格依赖于继承的mro列表,类名.mro()      ===>  不管有没有继承关系,super()都是根据mro往后查找
  b. 访问是绑定方法,有自动传值的效果

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


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

    def choose(self):
        print('%s choosing course' % self)


class Teacher(People):
    def score(self, stu, score):
        stu.score = score


stu = Student('张三', 12, 'male')
print(stu.__dict__)  # 查看对象的名称空间
方式二

七、继承背景下的属性查找

1、单继承背景下的属性查找

单继承背景下属性查找顺序:对象的名称空间------>对象的类的名称空间------->父类的名称空间

class Foo:
     x=333
     pass

class Bar(Foo):
#     x=222
     pass

obj=Bar()
# obj.x=111
print(obj.x)   # 333
单继承背景下的属性查找

2、多继承背景下的属性查找

多继承背景下属性查找的顺序:对象的名称空间------>对象的类的名称空间-------->按照从左往右的顺序一个一个的分支找下去

# 查找顺序是 obj-->A-->B-->E-->C-->F-->I-->D-->H

#第四层
class I:
    # x='I'
    pass

#第三层

class E:
    # x='E'
    pass

class F(I):
    # x='F'
    pass

class H:
    x='H'

# 第二层
class B(E):
    # x='B'
    pass

class C(F):
    # x='C'
    pass

class D(H):
    # x='D'
    pass

#第一层
class A(B,C,D):
    # x='A'
    pass

obj=A()
# obj.x=111
print(obj.x)
多继承背景下属性查找

八、继承顺序

1、在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)

2、如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性

如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先

九、组合

1、什么是组合

组合指的是某一个对象拥有一个属性,该属性的值是另外一个类的对象

class Foo():
    pass

class Bar():
    pass

obj=Bar()
obj.attrib=Foo()

2、组合的作用

通过为某一个对象添加属性(属性值是另外一个类的对象)的方式,可以间接地将两个类关联/整合/组合到一起,从而减少类与类之间代码冗余

3、组合的使用

class OldboyPeople:
    school = 'oldboy'

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


class OldboyStudent(OldboyPeople):

    def __init__(self, name, age, sex, score=0):
        super().__init__(name, age, sex)
        self.score = score
        self.courses = []

    def choose_course(self):
        print('%s choosing course' % self.name)

    def tell_all_course(self):
        print(('学生[%s]的课程如下' % self.name).center(60, '='))
        for course in self.courses:
            course.tell_course()
        print('=' * 80)


class OldboyTeacher(OldboyPeople):

    def __init__(self, name, age, sex, level):
        super().__init__(name, age, sex)
        self.level = level
        self.courses = []

    def score(self, stu, num):
        stu.score = num

    def tell_all_course(self):
        print(('老师[%s]教授的课程如下' % self.name).center(70, '-'))
        for course in self.courses:
            course.tell_course()
        print('-' * 80)


class Course:
    def __init__(self, c_name, c_price, c_period):
        self.c_name = c_name
        self.c_price = c_price
        self.c_period = c_period

    def tell_course(self):
        print('<课程名:%s 价格:%s 时间:%s>' % (self.c_name, self.c_price, self.c_period))


python = Course('python全栈开发', 10000, 5)
linux = Course('linux架构', 12000, 5)

stu = OldboyStudent('zs', 18, 'male')
stu.courses.append(python)
stu.courses.append(linux)
stu.tell_all_course()

teach = OldboyTeacher('egon', 18, 'male', 10)
teach.courses.append(python)
teach.tell_all_course()
组合的使用

软件重用的重要方式除了继承之外的另一种方式就是组合。

组合和继承都是有效的利用已有类的资源的重要方式,但使用场景和概念却都不同:

1、继承的方式:

  通过继承建立了派生类与基类之间的关系,是一种“是”的关系。

2、组合的方式:

  用组合的方式建立了类与组合的类之间的关系,是一种“有”的关系。

#当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合会更好。

原文地址:https://www.cnblogs.com/zhangbingsheng/p/10115017.html