继承,组合
继承
什么是继承
继承是一种关系: 描述两个类之间 什么是什么的关系
例: 麦兜,佩奇,猪悟能 都是猪
在程序中,继承描述的是类与类之间的关系
例如: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中出现
菱形继承时
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)