跳转至

继承派生

继承 inheritance 和 派生 derived

    什么是继承/派生
        1. 继承是从已有的类中派生出的新类,新类具有原类的数据属性和行为,并能扩展新的行为
        2. 派生类就是从一个已有的类中衍生出新的类,在新类的基础上,添加新的属性和行为

    为什么继承/派生
        1. 继承的目的是延续旧类的功能
        2. 派生的目的是在旧类的基础上改变原有的功能

    名词
        基类(base class) / 超类(super calss) / 父类(father class)
        派生类(drived class) / 子类(child class)

单继承

    语法
        class 类名(基类名):
            语句块
    说明
        单继承是指派生类由一个基类衍生出来的新类
# 此示例示意单继承的定义方法和用法
class Human:
    def say(self, what):
        print('说:', what)

    def walk(self, distance):
        print('走了', distance, '公里')

class Student(Human):
    def study(self, subject):
        print('正在学习%s' % subject)

class Teacher(Human):
    def teach(self, s):
        print('教:', s)

h1 = Human()
h1.say('今天天气真好')
h1.walk(5)

s1 = Student()
s1.walk(4)
s1.say('感觉有点累')
s1.study('python')

t1 = Teacher()
t1.walk(3)
t1.say('太累了,今晚吃啥')
t1.teach('面向对象')

继承派生机制的作用

  1. 可以将一些共有的功能加在基类中。实现代码的共享
  2. 在不改变基类的基础上改变原有的功能

继承说明

  • python3 任何类都直接或间接继承自object类
  • object类是一切类的超类
类的__base__属性
    __base__属性用来记录此类的父类

python中内建的类
    help(__builtins__)

覆盖override

什么是覆盖 覆盖是指在有继承关系的类中,子类中实现了与父类同名的方法,在子类的实例调用该方法时,实际调用的是子类中的覆盖版本

class A:
    def work(self):
        print('A.work 被调用')

class B(A):
    '''B类继承A类'''
    def work(self):
        print('B.work 被调用')

b = B()
b.work()  # 调用谁??

a = A()
a.work()  # 调用谁??
问题:
    当覆盖发生时,子类对象如何调用父类中的被覆盖方法??

    调用方式
        基类名.方法名(实例, 实际调用传参......)

super 函数

    super(cls, obj) 返回绑定超类的实例(要求obj 必须为cls类型的实例)
    super()  返回绑定超类的实例,等同于super(__class__, 实例方法的第一个参数,必须在方法内调用)

    作用
        借助super() 返回实例间接调用父类的覆盖方法
# 问题:
#     当覆盖发生时,子类对象如何调用父类中的被覆盖方法??
#
# 调用方式
#     super函数

class A:
    def work(self):
        print('A.work 被调用', self)

class B(A):
    '''B类继承A类'''
    def work(self):
        print('B.work 被调用', self)

    def super_work(self):
        #调用B类自己的work方法
        self.work()
        # 调用父类的work
        super(B, self).work()

b = B()
b.work()  # 调用谁??
super(B, b).work()

b.super_work()

显式调用基类的初始化方法

​ 当子类中实现了__init__方法,基类的构造方法并不会调用,此时需要显式调用

# 此示例示意显式调用初始化方法
class Human:
    def __init__(self, n, a):
        self.name = n
        self.age = a
        print('Human类的初始化方法被调用...')

    def infos(self):
        print('姓名:', self.name)
        print('年龄:', self.age)


class Student(Human):
    def __init__(self, n, a, s):
        super(Student, self).__init__(n, a)
        self.score = s

    def infos(self):
        super(Student, self).infos()
        print('成绩', self.score)


s1 = Student('张飞', 15, 100)
s1.infos()

用于类的函数

issubclass(cls, class_or_tuple) # 判断一个类是否继承自其他的类,如果此类cls是class或tuple中一个派生子类则返回True, 否则返回False

# 示例
        class A:
            pass
        class B(A):
            pass
        class C(B):
            pass
        issubclass(C, B)  # True
        issubclass(B, A)  # True
        issubclass(A, C)  # False

封装 enclosure

  • 封装是指隐藏类的实现细节,让使用者不关心这些细节
  • 封装的目的是让使用者通过尽可能少的方法(或属性)操作对象

私有属性和方法

    python类中,以双下划线('__')开头,不以双下划线结尾的标识符为私有成员,
    私有成员只能使用该类的方法来进行访问和修改
    1. 以__开头的属性为私有属性
    2. 以__开头的方法为私有方法

    注:
        python的封装是假的封装,也叫模拟的封装
# 此示例示意用私有属性和私有方法来实现封装

class A:
    def __init__(self):
        self.__p1 = 100  # << p1为私有属性
        self._p2 = 200

    def show_info(self):
        print(self.__p1)  # 此对象的实例方法可以修改此私有属性
        self.__a()  # 调用私有方法

    def __a(self):
        print('A类对象的__a方法被调用')

a = A()
# print(a.__p1)  # 不允许访问私有属性
print(a._p2)
a.show_info()
# a.__a()  # 出错,除A类的实例方法外,不能调用a对象的私有方法

多态 polymocphic

    字面意思: 多种状态
    多态指在有继承
    派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖方法的现象

程序的状态

    静态(编译时状态)
    动态(运行时状态)

    说明
        多态调用的方法与对象相关,不写类型相关,python全部对象都只有'运行时状态(动态)', 没有'C++'语言里编译时的状态(静态)
# 此示例示意静态和动态

class Shape:
    def draw(self):
        print('Shape的draw()被调用')

class Point(Shape):
    def draw(self):
        print('正在画一个点')

class Circle(Point):
    def draw(self):
        print('正在画一个圆')

def my_draw(s):
    s.draw()  #<<----- 此处显示出多态中的'动态'

s1 = Circle()  # 创建一个圆对象
s2 = Point()  # 创建一个点对象
my_draw(s1)
my_draw(s2)

多继承 multiple inheritance

    多继承是指一个子类继承两个或两个以上的基类

    语法
        class 类名(父类名, 父类名2....)
    说明
        一个子类同时继承自多个父类,父类中的方法可以同时继承下来
        如果两个父类中有同名方法,而在子类中没有覆盖此方法时,调用结果难以确定
# 此示例示意多继承的定义语法的基本用法
class Car:
    '''汽车类'''
    def run(self, speed):
        print('汽车以', speed, '公里/小时的速度行驶')

class Plane:
    '''飞机类'''
    def fly(self, height):
        print('飞机以海拔', height, '米的高度飞行')

class PlaneCar(Car, Plane):
    '''PlaneCar 同时继承自Plane类和Car类'''
    pass

pc = PlaneCar()  # 创建一个飞行汽车对象
pc.fly(10000)
pc.run(300)

多继承的缺陷

标识符冲突问题 (要谨慎使用多继承)

# 多继承的缺陷
# 小张写了一个类A
class A:
    def m(self):
        print('A.m()被调用')

# 小李写了一个类B
class B:
    def m(self):
        print('B.m()被调用')

# 小王感觉小张和小李写的类自己都可以用
class AB(A, B):
    pass

ab = AB()
ab.m()  # 请问调用谁,为什么

多继承的 MRO(Method Resolution Order) 问题

python3类的 __mro__属性
        作用
            用来记录类的方法的查找顺序
class A:
    def go(self):
        print('A')

class B(A):
    pass
    def go(self):
        print('B')
        super().go()

class C(A):
    pass
    def go(self):
        print('C')

class D(B, C):
    pass
    def go(self):
        print('D')
        super(D, d).go()

d = D()
d.go()

# super() 函数就是根据__mro__来调用上层的方法
练习
写一个农民类Peasant有方法:
        def farm(self, plant):
            ...
    写一个工人类Worker
        方法如下:
            def work(self, that):
                ...
    创建一个农民工为MigrantWorker, 让此类的对象拥有上面两个类的全部方法
    person = MigrantWorker()
    person.farm('水稻')  # 正在种植 水稻
    person.work('汽车')  # 正在制造 汽车

    查看各个类的__mro__属性

函数重写 overwrite

什么是函数重写

在自定义的类添加相应的方法,让自定义的类创建的实例能够使用内建函数一样进行操作

对象转字符串函数

    repr(x)
        返回一个能表示python对象的表达式字符串,通常eval(repr(obj)) == obj
    str(x)
        通过给定的对象返回一个字符串(这个字符串通常供人阅读)

    示例
        s = 'I'mTeacher'
        print(str(s))  # I'mTeacher
        print(repr(s))  # 'I\'mTeacher'

对象转字符串函数的重写方法

    repr() 函数的重写方法
        def __repr__(self):
            return 字符串

    str() 函数的重写方法
        def __str__(self):
            return 字符串

    str(obj) 函数调用方法说明
        1. str(obj) 函数先查找obj.__str__(方法),调用此方法并返回结果
        2. 如果obj.__str__()方法不存在,则调用obj.__repr__()方法并返回结果
        3. 如果obj.__repe__方法不存在,则调用object类的__repr__实例方法显示<__main__.xxxx object at 0xXXXXXXXXXX> 格式的字符串
# 此示例示意对象转字符串函数的重写方法
class MyNumber():
    def __init__(self, data):
        self.data = data  # 在每个对象内部都创建一个实例变量来绑定数据

    def __str__(self):
        return '自定义的数字:%d' % self.data

    def __repr__(self):
        '''返回的字符串一定是能表达self对象的表达式'''
        return 'MyNumber(%d)' % self.data

n1 = MyNumber(100)
print('str(n1) =', str(n1))
print('repr(n1) =', repr(n1))

n2 = MyNumber(200)
print(n2)  # 在print内部会将n2用str(n2) 转为字符串写到sys.stdout
print(str(n2))
print(repr(n2))

其他内建函数的重写(部分)

    方法名                                函数名
   def __abs__(self):             abs(obj)  函数调用
   def __len__(self):             len(obj)  函数调用
   def __reversed__(self):        reversed(obj)  函数调用
   def __round__(self):           round(obj)  函数调用
class MyList:
    '''这是一个自定义的列表类型,此类型的对象用data属性绑定的列表来存储数据'''
    def __init__(self, iterable=()):
        self.__data = [x for x in iterable]

    def __len__(self):
        return len(self.__data)

    def __str__(self):
        return '%s' % self.__data

    def __repr__(self):
        return 'MyList(%s)' % self.__data

myl = MyList(range(10))
print(myl)
print(len(myl))

数值转化函数的重写

    def __complex__(self):                      complex(obj)  函数调用
    def __init__(self):                         int(obj)  函数调用
    def __float__(self):                        float(obj)  函数调用
    def __bool__(self):                         bool(obj)  函数调用
# 此示例示意对象数值转换函数的重写方法
class MyNumber():
    def __init__(self, data):
        self.data = data  # 在每个对象内部都创建一个实例变量来绑定数据

    def __repr__(self):
        '''返回的字符串一定是能表达self对象的表达式'''
        return 'MyNumber(%d)' % self.data

    def __int__(self):
        '''重写int(obj)函数'''
        return int(self.data)

    def __float__(self):
        return float(self.data)

n1 = MyNumber(100)
n = int(n1)
f = float(n1)
print(f)
c = complex(n1)  # 当没有n1.__complex__() 时会调用n1.__float__() + 0j 默认查找
print(c)

布尔测试函数的重写

    格式
        def __bool__(self):
            ...
    作用
        用于bool(obj) 函数取值
        用于if语句真值表达式中
        用于while语句的真值表达式中
    说明
        1. 当自定义类有__bool__(self)方法时,此方法的返回作为bool(x)的返回值
        2. 当不存在__bool__(self) 方法时,返回__len__(self) 方法的返回值是否为非零来测试布尔值
        3. 当不存在__len__(self) 方法时,则直接返回True

对象的属性管理函数

    getattr(obj, name[, defalut])  # 从一个对象用字符串name 得到对象的属性,         getattr(x, 'y')
    等同于x.y; 当属性不存在时,如果给定default参数则返回default,如果没有给出default则触发一个AttributeError的错误

    hasattr(obj, name, value)
    用给定的name 字符串返回obj 是否有此属性,此种做法可以避免在gatattr(obj, name)时引发错误

    setattr(obj, name, value)
    给对象obj的名为name的属性设置相应的值value,setattr(x, 'y', v)等同于 x.y = v

    delattr(obj, name) 删除对象obj中的name属性, del(x, 'y') 等同于 del x.y