一、继承简述及基本语法
# 1.继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以
# 称为基类和超类,新建的类称为派生类和子类
# python中类的继承分为:单继承和多继承
2.为什么要使用继承:继承的一方可以直接使用被继承一方已经有的东西其目的是为了重用已经有的代码,提高重用性
class A(object): pass # 父类,基类,超类 class B: pass # 父类,基类,超类 class A_son(A, B): pass # 子类,派生类 class AB_son(A): pass # 一个类可以被多个类继承 # 一个类可以继承多个父类 仅在python里 print(A_son.__bases__) print(AB_son.__bases__) print(A.__bases__) # 没有继承父类默认继承object class Animal: def __init__(self, name, aggr, hp): self.name = name self.aggr = aggr self.hp = hp def eat(self): self.hp += 100 print("回血") class Person(Animal): def __init__(self, name, aggr, hp, sex): Animal.__init__(self, name, aggr, hp) self.hp = hp # 派生属性 class Dog(Animal): def __init__(self, name, aggr, hp, type): # Animal.__init__(self, name, aggr, hp) super().__init__(name, aggr, hp) # 只在新式类中有,python中都是新式类 self.type = type # 派生属性 def eat(self): # Animal.eat(self)# 如果既想要实现新的功能,又想再子类调用父类 super().eat() self.teeth = 2 def bite(self, person): # 派生方法 person.hp -= self.aggr jin = Dog("金老板", 100, 500, "吉娃娃") alex = Person("tank", "100", 600, "畜生") super(Dog, jin).eat() # 在外面掉用需要传类和对象 print(alex.hp) jin.eat() print(jin.hp) # 父类中没有的属性,在子类中出现,就叫派生属性 # 父类中没有的方法,在子类中出现,就叫派生方法 # 只要是子类的对象调用,子类中有的名字一定要用子类的,子类中没有才找父类,如果父类也没有就报错 # 如果父类 子类都有,就用子类 # 如果还想用父类的,单独调用父类的,需要自己传self参数
二、继承与抽象
抽象:不具体,不清晰,很模糊,看不懂
将多个子类中相同的部分,进行抽取,形成一个新的类,这个过程也称之为抽象的过程
正确的使用继承:
1.先抽象在继承
2.继承一个已经现存的类,扩展或是修改原始的功能
# 抽取老师和学生中相同的部分形成person类 class Person: def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender def say_hi(self): print("name:%s,gender:%s,age:%s" % (self.name,self.gender,self.age)) class Teacher(Person): def teaching(self): print("老师教学生,写代码....") t1 = Teacher("jack","male",20) t1.say_hi() class Student(Person): pass stu1 = Student("rose","female",18) stu1.say_hi()
三、属性的查找顺序
# class A: # text = "haha" # # class B(A): # text = "heihei" # pass # # b = B() # print(b.text) # heihei
对象自己的 - > 所在类中 -> 找父类 - >父类的父类 ->Object
四、派生与覆盖
派生:就是父类里面没有的,子类有称为派生
覆盖:也称之为重写 overrides,当子类出现了与父类名称完全一致的属性或是方法
class Person: def say_hi(self): print("hello") class Student(Person): def say_hi(self): print("hello world!") stu = Student() stu.say_hi()
五、子类调用父类
class Parent: text = "abc" def say_something(self): print("anything") class Sub(Parent): def show_info(self): # print(super(Sub,self).text) # super(Sub,self).say_something() # 访问方式2 py3的新语法 最常用的方式 print(super().text) super().say_something() #方式3 直接指定类名调用 # print(Parent.text) # Parent.say_something(self) # sub = Sub() # sub.show_info()
六、初始化方法必须调用
# 为什么要在初始化方法中调用 父类的初始化方法 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): def __init__(self,name,gender,age,number): 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,"old01") stu.say_hi() # 如果子类
七、实现一个可以限制元素列表类型
需求 实现一个能够限制元素类型的列表类 """ class MyList(list): def __init__(self,element_type): super().__init__() # 调用父类的初始化方法 来完成基本的初始化 self.element_type = element_type def append(self, object): """ :param object: 是要存储的元素 :return: 没有 """ if type(object) == self.element_type: #我们需要在这里访问父类的append函数来完成真正的存储操作 super(MyList,self).append(object) else: print("sorry sir, you element type not is %s" % self.element_type) # 创建是指定要存储的元素类型 m = MyList(int) # 当你有需求,是需要在创建对象时 干点什么事儿 那就该想到初始化方法 m.append(1) print(m[0]) m.append("121212")
#### 强调在强调:
当你继承一个现有的类,并且你覆盖了父类的init方法时,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需的参数
八、组合
组合:一个对象的属性是另一个类的对象
##### 组合的目的:
也是为了重用现有代码
什么时候使用继承:分析两个类的关系,到底是不是:什么是什么的关系
什么时候使用组合:如果两个类之间 没有太大的关系,完全不属于同类
另外组合相比继承,耦合度更低了
九、钻石继承
继承顺序
继承原理
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
继承小结
继承的作用
减少代码的重用 提高代码可读性 规范编程模式
## 菱形继承
首先明确python支持多继承
##### 补充:新式类与经典类
python3中任何类都是直接或间接继承了Object
新式类,任何显式或隐式地继承自object的类就称之为新式类, python3中全都是新式类
经典类,既不是Object的子类 ,仅在python2中出现
当出现了菱形继承时,新式类,先深度,当遇到了共同父类时就广度
新式类,就是深度优先
class Dog: def __init__(self, name, aggr, hp, kind): # lcs = locals() # lcs.pop("self") # self.__dict__.update(lcs) self.name = name self.aggr = aggr self.hp = hp self.kind = kind def bite(self, person): person.hp -= self.aggr class Person: def __init__(self, name, aggr, hp, kind): self.name = name self.aggr = aggr self.hp = hp self.kind = kind self.money = 1000 def attack(self, dog): dog.hp -= self.aggr print(dog.hp) def get_weapon(self, weapon): if self.money >= weapon.price: self.money -= weapon.price self.weapon = weapon # 相当于把武器所有的属性赋予了人,不过weapon还是属于Weapon对象 self.aggr += weapon.aggr else: print("余额不足,青葱之") class Weapon: def __init__(self, name, aggr, njd, price): self.name = name self.aggr = aggr self.njd = njd self.price = price def kills(self, person): if self.njd > 0: person.hp -= self.aggr * 2 self.njd -= 1 w = Weapon("狼牙棒", 100, 3, 998) alex = Person("alex", 0.5, 100, "男") jin = Dog("鸡哥", 0.5, 500, "不男不女") alex.get_weapon(w) print(alex.weapon) # <__main__.Weapon object at 0x000001C514D24978> 是武器的对象 所有的武器属性都可用 print(alex.aggr) alex.attack(jin) alex.weapon.kills(jin) print(jin.hp) alex.attack(jin) # 创建一个老师类,老师有生日,生日也可以是一个类,组合 class Birthday: def __init__(self, year, month, day): self.year = year self.month = month self.day = day def nima(self): print("erguo") class Course: languge = "Chinese" def __init__(self, teacher, couser_name, period, price): self.teacher = teacher self.couser_name = couser_name self.period = period self.price = price def func(self): pass class Tearcher: def __init__(self, name, age, sex, birthday): self.name = name self.age = age self.sex = sex self.birthday = birthday self.birthday = b self.course = Course("jason", "python", "半年", "20000") b = Birthday(2019, 1, 16) egg = Tearcher("shabi", "18", "男", b) # self.birthday =b 可以调用任何b里面所有属性 print(egg.course.couser_name) print(egg.course.period) print(egg.course.couser_name) print(egg.course.price) # print(egg.name) # print(egg.birthday.year) # print(egg.birthday.month) print(egg.birthday.day) print(egg.birthday.nima()) # 通过Birthday的对象调用方法