继承,组合 继承 组合 菱形继承

什么是继承

继承是一种关系: 描述两个类之间 什么是什么的关系

例: 麦兜,佩奇,猪悟能 都是猪

在程序中,继承描述的是类与类之间的关系

例如:a继承了b,a就能直接舒勇b已经存在的属性方法

a称为子类,b称为父类(基类)

为什么要使用继承:

继承的一方(子类)可以直接使用被继承一方(父类)已经有的东西(属性和方法)

目的: 重用已有的代码,提高重用性

如何使用:

语法:

class 类名称(父类1名称,父类2名称,...)
    代码块
# python中,一个子类可以同时继承多个父类

例:

class Sup:
    info = 'from Sup'
    def money(self):
        print('from Sup money')
​
class Sub(Sup):  # Sub 继承 Sup
    pass
obj = Sub()
# 子类调用父类中的属性
print(obj.info)  # >>> from Sup
# 子类调用父类中的方法
obj.money()  # >>> from Sup money

抽象:

名词解释:不具体,看不清,很模糊,看不懂

抽象的过程:

1.将多个类中相同的部分(属性或方法),进行抽取,

2.创建一个新的类,把这些相同的部分放到新的类中(这个新类是被抽取的类的大类,)

正确的使用继承:

1.先抽象再继承

2.继承一个已经存在的类,扩展或修改原始功能

图解:继承,组合
继承
组合
菱形继承

继承,组合
继承
组合
菱形继承

例:老师类和学生类相同的部分有name,age,gender...,老师和学生都属于人,就可以创建一个新类:人类

class Teacher:
    func = 'teach'
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
​
    def say_hi(self):
        print('name:%s, age:%s, gender:%s'%(self.name, self.age, self.gender))
​
t = Teacher('haha', 20, 'male')
print(t.func)
t.say_hi()
​
​
class Student:
    func = 'play'def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
​
    def say_hi(self):
        print('name:%s, age:%s, gender:%s' % (self.name, self.age, self.gender))
​
s = Student('xixi', 15, 'male')
print(s.func)
s.say_hi()
​
# 抽象后的到:
​
​
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
    def say_hi(self):
        print('name:%s, age:%s, gender:%s' % (self.name, self.age, self.gender))
​
​
class Teacher(Person):
    func = 'teach'
    def skill_training(self):
        print('教 Python 代码')
        
t = Teacher('haha', 20, 'male')
print(t.func)  # >>> teach
t.say_hi()  # >>> name:haha, age:20, gender:male
t.skill_training()  # >>> 教 Pythong 代码
class Student(Person):
    func = 'play'
​
s = Student('xixi', 15, 'male')
print(s.func)  # >>> play
s.say_hi()  # >>> name:xixi, age:15, gender:male

属性的查找顺序

对象自己 ---> 所在类 ---> 父类 ---> Object

class A:
    func = 'haha'
class B(A):
    func = 'xixi'
obj = B()
obj.func = 'hehe'
print(obj.func)  # >>> hehe

派生

当一个子类中出现了父类中不同的内容(属性或方法)时,这个子类就称之为派生类

通常子类都会写一些新的代码,不会和父类完全相同,即通常同时派生类

class sup:
    text = 20
    def func(self):
        print('hello')
class sub(sup):
    def auth(self):
        print('hello, world')

 

覆盖

也称之为重写 overrides

当子类中出现了与父类中 名称 完全一致的属性会方法

父类中被覆盖的属性会方法子类无法再调用,因为查找顺序:对象-->子类-->父类

class sup:
    text = 20
    def func(self):
        print('hello')
class sub(sup):
    # 覆盖父类中的func函数
    def func(self):
        print('hello, world')

练习 : 创建一个可以限制元素类型的列表*

# 把list当做父类,可以使用list中已经写好的代码,不需要再重新写
class MyList(list):
    def __init__(self, my_type):
        # 调用父类的初始化方法,来完成基本的初始化
        super().__init__()  # 因父类的init方法不需要参数,所以不用传参
        self.my_type = my_type
​
    def append(self, obj):
        '''
        :param obj:  要添加的元素
        :return: 没有返回值
        '''
        if type(obj) == self.my_type:
            # 调用父类(list)里的append方法,来完成真正的存储工作
            super(MyList, self).append(obj)
            # 也可用 super().append(obj)
        else:
            print('添加的元素类型与设定的%s类型不同'%self.my_type)
​
# 当你的需求是在创建对象时干点什么事,就该想到__init__初始化方法
m = MyList(int)  # 元素定义为 int 类型
m.append(2)
m.append(4)
m.append(6)
print(m)  # >>> [2, 4, 6]
print(m[1])  # 4
m.append('1')  # 添加的元素类型与设定的<class 'int'>类型不同

在子类中访问父类的内容

语法:

方式1:
   super(当前类名,self).要调用的父类的属性或方法
方式2:
   super().要调用的父类的属性或方法
   # 方式2 属于python3语法
方式3:
   父类名.要调用的父类的属性或方法(self)
   # 方式3余继承无关
class Animal:
    def __init__(self, name, age, hp):
        self.name = name
        self.age = age
        self.hp = hp

    def eat(self):
        print('吃药回血')

class Dog(Animal):
    def __init__(self, name, age, hp, colour):
        # 调用父类的__init__ 方法一:
        Animal.__init__(self, name, age, hp)
        # 调用父类的 __init__ 方法二:
        super().__init__(name, age, hp)
        self.colour = colour  # 派生属性

    def eat(self):
        print('吃药回蓝')
d = Dog('二哈', 2, 100, 'black')
d.eat()  # 吃药回蓝
super(Dog, d).eat()  # 吃药回血
 

注意(**)

当你继承一个现有的类,并且你覆盖了父类中的双下init方法时,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需要的参数

class Person:
    def __init__(self,name,gender,age,*args):
        self.name = name
        self.gender = gender
        self.age = age
        self.aa()  # 会执行此函数
def aa(self):
        print("aa run")
​
    def say_hi(self):
        print("name:%s ,gender:%s,age:%s" % (self.name,self.gender,self.age))
​
​
class Student(Person):
    # 覆盖父类的 __init__ 
    def __init__(self,name,gender,age,number):
        # 调用父类的 __init__ 并传参
        super().__init__(name,gender,age)
        self.number= number
​
    def say_hi(self):
        # 调用父类方法
        super().say_hi()
        print("numnber:%s" % self.number)
​
stu = Student("rose","mael",20,"01")
stu.say_hi()
>>>
aa run
name:rose ,gender:mael,age:20
numnber:old01

组合

组合是一种关系 : 描述两个类之间 什么有什么的关系

例 : 学生有手机,游戏角色有装备...

将一个类的对象作为另一个类的属性传入

什么时候使用继承: 分析两个类之间的关系,到底是不是 什么是什么的关系

什么时候使用组合: 分析两个类之间的关系, 没有太大的关系,完全不属于同类

组合比继承的耦合性更低

 

菱形继承

首先声明 Python 支持多继承 可以通过 子类名.mro() 查看父类的查找顺序,返回的是一个列表

新式类与经典类

python3中的任何类型都是直接或间接的继承Object

新式类: 任何显式或隐式地继承自Object的类就称之为新式类,python3中都是新式类

经典类:既不是Object的子类,仅在python2中出现

菱形继承时

1新式类,先查找深度,当遇到了共同的父类时就查找广度

2.经典类: 按深度查找

 

 

练习:

"""
程序员
姓名 性别 年龄 工资 编程技能
和项目经理
多了奖金,管理技能
项目经理也是程序员晋升而来
将程序员信息和管理员信息写入文件各自的类别文件中
"""
import os
import pickle

# 获取到 项目 的路径
BASE_PATH = os.path.dirname(__file__)
# 拼接 db 文件夹的路径
DB_PATH = os.path.join(BASE_PATH,'db')

class File:

    # 写文件功能
    def save(self):
        # 类 文件的名
        cls_name = self.__class__.__name__
        # 拼接  类文件的路径
        cls_path = os.path.join(DB_PATH,cls_name)
        # 判断  类文件是否存在
        if not os.path.exists(cls_path):
            # 若不在 创建文件夹
            os.mkdir(cls_path)
        # 拼接  类文件夹中文件的路径
        cls_name_path = os.path.join(cls_path,self.name)
        # 将 程序员 文件写入程序员文件夹中
        with open(cls_name_path,'wb') as f:
            # 序列化到文件中
            pickle.dump(self, f)
            f.flush()

    # 读文件功能
    def get_info(self, name):
        # 拼接 所查找的类的文件夹路径
        check_cls_path = os.path.join(DB_PATH,self.__class__.__name__)
        # 拼接 所查找的文件路径
        check_name_path = os.path.join(check_cls_path,name)
        # 判断 类文件夹下是否有此文件
        if os.path.exists(check_name_path):
            # 读出文件
            with open(check_name_path,'rb') as f:
                res = pickle.load(f)
                return res


# 程序员的类
class Coder(File):
    def __init__(self, name, gender, age, salary):
        self.name = name
        self.gender = gender
        self.age = age
        self.salary = salary

    # 程序员的功能
    def programming(self):
        print('正在开发项目...')

# 项目经理的类 项目经理从程序员而来,属于程序员,同时又有其他功能 所以是继承
class Manager(Coder):
    def __init__(self, name, gender, age, salary, bonus):
        super(Manager, self).__init__(name, gender, age, salary)
        self.bonus = bonus
    def manage(self):
        print('正在与程序员友好的沟通中...')

c = Coder('X', 'man', 20, 100)
c.programming()
c.save()
res = c.get_info('X')
print(res)
print(res.name)
print(res.gender)
print(res.age)
print(res.salary)

print('---------------------------------------------------------')

m = Manager('Y', 'woman', 21, 200, 50000)
m.manage()
m.save()
res = m.get_info('Y')
print(res)
print(res)
print(res.name)
print(res.gender)
print(res.age)
print(res.salary)
print(res.bonus)
答案