类的继承

一、继承介绍

1.什么是继承?

  • 继承是一种新建类的方式, 新建的类可以继承一个或多个父类, 那么这时这个新建的类就被称之为子类或派生类, 父类又被成之为基类或超类,

  • 继承的特性:子类会遗传父类的属性

  • 继承是类与类之间的关系

  • 注意: python支持多继承, 在python中新创建的类可以继承一个或多个父类(补充: python中支持多继承, 其它大部分语言都不支持.)

类(对象)的继承基本语法格式

  • python中一切皆对象,类也是对象

  • python中支持一个类同时继承多个父类(多继承)

    class Parent1(object):
        x = 1111
    
    
    class Parent2(object):
        pass
    
    
    class Sub1(Parent1):  # 单继承
        pass
    
    
    class Sub2(Parent1, Parent2):  # 多继承
        pass
    
  • 查看当前类的所继承的父类: 通过内置属性类名.__bases__可以查看类继承的所有父类

    print(Sub1.__bases__)  # (<class '__main__.Parent1'>,)
    print(Sub2.__bases__)  # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)
    
    print(Sub2.x)  # 1111
    
  • 补充宝典:python为类内置的特殊属性

    类名.__name__ # 类的名字(字符串)
    类名.__doc__ # 类的文档字符串
    类名.__base__ # 类的第一个父类
    类名.__bases__ # 类所有父类构成的元组,仅查看直接父类。
    类名.__dict__ # 类的属性字典
    类名.__module__ # 类定义所在的模块
    类名.__class__ # 实例对应的类(仅新式类中)
    

2.为什么要用继承?

  • 继承的作用:用来解决类与类之间代码冗余问题
  • 类的作用:用来解决对象与对象之间代码冗余问题

3.继承的实现

  • 不使用继承时编写多个类,类与类之间存在冗余问题
class Animal:
    def run(self):
        print("奔跑")
    
    def eat(self):
        print("吃东西")
        
class Duck:
    def run(self):
        print("奔跑")
        
    def eat(self):
        print("吃东西")
    
    def speak(self):
        print("嘎嘎嘎")

class Pig:
    def run(self):
        print("奔跑")

    def eat(self):
        print("吃东西")

    def speak(self):
        print("咕咕咕")

class Person:
    def run(self):
        print("奔跑")

    def eat(self):
        print("吃东西")

    def speak(self):
        print("呵呵呵")
   
  • 使用使用继承编写多个类,解决类与类之间的冗余问题

    # 可以明显感觉到代码量减少    
    class Animal:
        def run(self):
            print("奔跑")
    
        def eat(self):
            print("吃东西")
            
    class Duck(Animal):
        def speak(self):
            print("嘎嘎嘎")
        
    class Pig(Animal):
        def speak(self):
            print("咕咕咕")
            
    class Person(Animal):
        def speak(self):
            print("呵呵呵")
    

4.python中的多继承

在平时应用中:

不建议使用多继承, 有可能会引发可恶的菱形问题. 如果真的涉及到一个子类不可避免地重用多个父类的属性, 也不应该直接使用多继承, 而是应该使用Mixins机制

优点:

  • 子类可以同时遗传多个父类的属性, 最大限度的解决类与类之间的代码重用问题

缺点:

继承应该表达的是一种什么"是"什么的关系. 违背了这种思维逻辑就会引发下面的问题.

  1. 多继承违背了人的思维逻辑习惯
  2. 代码的可读性会变差
  3. 扩展性变差(可读性和扩展性是相辅相成的)

二、属性查找顺序

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

  • 对象的属性 (attribute) : 我们一般是指对象的属性和方法
  • 多层继承关系的属性查找顺序, 永远是从最下层开始找,调用自己self.[属性]
  • 以下示例的数查找顺序 : 对象自己----->对象的类----->父类----->父类
  • 强调:谁来调用,查找方式就以谁作为起始位置开始查找

ps : Ctrl + 鼠标右键 快速跳转到属性 / 方法位置, 有时并不准确

'''
由对象发起的属性查找: 会从对象自身的属性里检索, 没有则会按照对象的类"类名.mro()"规定的顺序依次找下去   
由类发起的属性查找: 会按照当前类"类名.mro()"规定的顺序依次找下去 
'''
# 示例一:
class Foo:
    def f1(self):
        print("FOO.f1")

    def f2(self):
        print("FOO.f2")  # obj.f1() # "obj"来调用, 执行查找方式就以"obj"作为起始位置开始查找
        self.f1()


class Bar(Foo):
    def f1(self):
        print("Bar.f1")


obj = Bar()
obj.f2()
# 查找顺序: obj.f2 -> Bar.f2 -> Foo.f2 --> Foo中找到了f2 -> obj.f1 -> Bar.f1 -> Bar中找到了f1


'''输出结果
FOO.f2
Bar.f1
'''

# 示例二:

class Foo:
    def f1(self):
        print("FOO.f1")

    def f2(self):
        print("FOO.f2")  #调用当前类中的f1
        Foo.f1(self)


class Bar(Foo):
    def f1(self):
        print("Bar.f1")


obj = Bar()
obj.f2()
'''输出结果
FOO.f2
FOO.f1
'''

# 示例三
class Foo:
    def __f1(self):  # _Foo__f1
        print("FOO.f1")

    def f2(self):
        print("FOO.f2")  # 调用当前类中的f1
        self.__f1()  # self.__f1


class Bar(Foo):
    def __f1(self): # _Bar__f1
        print("Bar.f1")


obj = Bar()
obj.f2()
'''输出结果
FOO.f2
FOO.f1
'''

2.多继承属性查找顺序

  • 多继承存在属性重复问题, 多个父类中存在着相同的属性, 那么对象该选择哪个属性使用呢?

  • 按照右边顺序查找 : 对象自己----->类----->父类 (多个父类,从左到右的先后顺序来找)

    class Wo:
        def foo(self):
            print("哎呀!")
    
    
    class Yao:
        def foo(self):
            print("吃多了长肉")
    
    
    class Jian:
        def foo(self):
            print("少吃又不瘦")
    
    
    class Fei:
        def foo(self):
            print("我还是吃吧~~~")
    
    
    class People(Wo, Yao, Jian, Fei):  # 继承了四个父类,查找顺序:从左到右
        pass
    
    
    abandon = People()
    abandon.foo()  # 哎呀! (找到的是最左边的)
    

三、新生类与经典类

1.新生类

  • 继承了 object 的类以及该类的子类子子类, 都是新式类 (Python3中统一都是新式类)
  • Python3 中如果一个类没有继承任何类, 则默认会继承 object 类, 也就是Python3中所有的类都是新式类
#在python3中
class Student(object):  # Student就叫新式类
    pass


class Teacher():  #默认继承"object"
    pass


class School(Student, Teacher):
    pass


# 查看一个类,继承了哪些父类
print(School.__bases__)  # (<class '__main__.Student'>, <class '__main__.Teacher'>)
print(Teacher.__bases__)  # (<class 'object'>,)
print(Student.__bases__)  # (<class 'object'>,)

2.经典类

  • 没有继承 object 的类以及该类的子类子子类, 都是经典类 (只有Python2中才区分新式类和经典类)

  • Python2 中如果一个类没有继承任何类, 它不会继承object 类, 所以Python2 中才有经典类

    # 如果在python2中
    #coding:utf-8  #在python2中需要在最顶行指定字符编码
    class Student(object):  # 新式类
        pass
    class Teacher():       # 经典类
        pass
    #py2的语法
    print Student.__bases__  #(<type 'object'>,)
    print Teacher.__bases__  #()
    

四、继承与抽象

  • 继承描述的是子类与父类之间的关系,是一种什么是什么的关系
  • 要找出这种关系,必须先抽象再继承,抽象即抽取类似或者说比较像的部分

1.抽象

  • 将佩奇和乔治两对象比较像的部分抽取成类
  • 将人,鸭,猪这三个类比较像的部分抽取成父类
  • 抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

类的继承_类学习

2.继承

  • 基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构

类的继承_Python开发_02

继承应用:

class People:
    school = "清华大学"

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


class Student(People):
    def choose(self):
        print(f"{self.name}正在选课~~~")


class Teacher(People):
    def teacher(self):
        print(f"I am {self.name} teacher")


# Teacher类内并没有定义__init__方法,但是会从父类中找到__init__,因而仍然可以正常实例化,如下
teacher1 = Teacher('淘小欣', 'male', 18)
print(teacher1.__dict__)  # {'name': '淘小欣', 'sex': 'male', 'age': 18}