前两天我们认识了面向对象也对面向对象有了初步的认识今天我们先会说一点组合的进阶,今天重点是继承。
一、组合
组合只有一个例子因为组合只作为上一章的补充内容
#老师 课程 生日 class Course: def __init__(self,name,period,price): self.name = name self.period = period self.price = price class Birth: def __init__(self,year,month,day): self.year = year self.month = month self.day = day class Teacher: def __init__(self,name,salary,boy_friend,python): self.name = name self.salary = salary self.bf = boy_friend self.course = python python = Course(‘python‘,‘6 months‘,20000) egg = Teacher(‘egon‘,200,‘yuan‘,python) print(egg.bf) print(egg.name) print(egg.course.name) egg_birth = Birth(1965,2,2) print(egg_birth.year) egg.birth = egg_birth print(‘***‘,egg.birth.year)
组合
二、继承
1、何为继承
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。
面向对象编程语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
继承
查询继承
SubClass1.__bases__ #查看所有继承的父类
如果没有指定基类,python的类会默认继承object类,object是所有python类的基类
2、继承与抽象
继承 是从大范围到小范围
抽象 小范围到大范围
抽象即抽取类似或者说比较像的部分。
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
3、继承与重用性
class Animal: ‘‘‘ 人和狗都是动物,所以创造一个Animal基类 ‘‘‘ def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): print(‘%s is eating‘%self.name) class Dog(Animal): pass class Person(Animal): pass egg = Person(‘egon‘,10,1000) ha2 = Dog(‘二愣子‘,50,1000) egg.eat() ha2.eat()
代码重用
用已经有的类建立一个新的类,这样就重用了已经有的类中的一部分甚至大部分,大大节省了编程工作量,这就是重用
4、派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
class Animal: ‘‘‘ 人和狗都是动物,所以创造一个Animal基类 ‘‘‘ def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): print(‘%s is eating‘%self.name) class Dog(Animal): ‘‘‘ 狗类,继承Animal类 ‘‘‘ def bite(self, people): ‘‘‘ 派生:狗有咬人的技能 :param people: ‘‘‘ people.life_value -= self.aggressivity class Person(Animal): ‘‘‘ 人类,继承Animal ‘‘‘ def attack(self, dog): ‘‘‘ 派生:人有攻击的技能 :param dog: ‘‘‘ dog.life_value -= self.aggressivity egg = Person(‘egon‘,10,1000) ha2 = Dog(‘二愣子‘,50,1000) print(ha2.life_value) print(egg.attack(ha2)) print(ha2.life_value)
派生
像ha2.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找...直到最顶级的父类。
子类执行父类的方法可以直接用super方法,或者用调用普通函数的方式,即:类名.func()
class Animal: #父类 基类 超类 def __init__(self,name,life_value,aggr): self.name = name self.life_value = life_value self.aggr = aggr #攻击力 def eat(self): self.life_value += 10 class Person(Animal): #子类 派生类 def __init__(self,money,name,life_value,aggr): super().__init__(name,life_value,aggr) self.money = money #派生属性 def attack(self,enemy): #人的派生方法 enemy.life_value -= self.aggr class Dog(Animal): #子类 派生类 def __init__(self,breed,name,life_value,aggr): #Animal.__init__(self,name,life_value,aggr) #让子类执行父类的方法,就是父类名.方法名(参数),连self也得传 super().__init__(name,life_value,aggr) #super关键字——新式类 #super(Dog,self).__init__(name,life_value,aggr) #super关键字——新式类 self.breed = breed def bite(self,person): #狗的派生方法 person.life_value -= self.aggr def eat(self): # 父类方法的重写 super().eat() print(‘dog is eating~~~ ‘) ha2 = Dog(‘牛头梗‘,‘旺财‘,20000,100) print(ha2.life_value) ha2.eat() print(ha2.life_value) # super(Dog,ha2).eat() #调用父类的 print(ha2.life_value)
super
5、小结
在继承中 继承的语法: class 类名(父类名): 想在子类中实现调用父类的方法 在类内 ——super(子类名,self).方法名() 在类外面 ——super(子类名,对象名).方法名() 如果不指定继承的父类,默认继承object 子类可以使用父类的所有属性和方法 如果子类有自己的方法就执行自己的的 如果是子类没有的方法就执行父类的 如果子类父类都没有这个方法就报错 继承、抽象、派生 继承 是从大范围到小范围 抽象 小范围到大范围 派生 就是在父类的基础上又产生子类——派生类 父类里没有的 但子类有的 ——派生方法,派生属性 方法的重写 父类里有的方法,在子类里重新实现
小结
三、抽象类与接口类
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。
1、多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口
接口隔离原则: 使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
2、方法的实现
1)在抽象类中,我们可以对一些抽象方法做出基础实现;
from abc import ABCMeta,abstractmethod class Animal(metaclass=ABCMeta): @abstractmethod def eat(self): print(‘打开粮食的袋子‘) print(‘放一个吃饭的碗‘) print(‘把粮食倒在碗里‘) @abstractmethod def sleep(self): pass class Dog(Animal): def eat(self): super().eat() print(‘dog is eating‘) def sleep(self): print(‘dog is sleeping‘) d = Dog() d.eat()
抽象类
抽象类:
抽象: 从小范围到大范围
2)而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
from abc import ABCMeta,abstractmethod class Payment(metaclass = ABCMeta): #metaclass --> 元类 @abstractmethod def pay(self,money):pass class Applepay(Payment): def pay(self,money): print(‘apple pay 支付了%s‘%money) class Alipay(Payment): def pay(self,money): print(‘支付宝 支付了%s‘%money) class Wechatpay(Payment): def fuqian(self,money): print(‘微信支付了%s‘%money) def payment(pay_obj,money): pay_obj.pay(money) apple1 = Applepay() ali1 = Alipay() wechat1 = Wechatpay() payment(wechat1,200)
接口类
接口类:
约束继承接口类的子类必须实现被abstractmethod装饰的方法
在接口类中不要做实现(不要出现函数体)
接口类不能被实例化
3、小结
1)当几个子类的父类 有相同的功能需要被实现的时候 就用抽象类
当几个子类 有相同的功能 但是实现各不相同的时候 就用接口类
抽象类和接口类的本质作用:约束
2)抽象类
在Python里 默认是有的
父类的方法 子类必须实现
抽象类 不能被实例化
抽象类内的方法 可以被简单实现
3)接口类(在抽象类的基础上)
在python里 默认是没有的
接口类中的方法 不能被实现
原则:抽象类最好不要用多继承,而接口类可以
四、钻石继承
继承顺序
python如果继承了多个类寻找方法有两个方法深度优先(经典类)和广度优先(新式类),而python3中没有经典类了,所以我们不用考虑多重情况。
class A(object): def test(self): print(‘from A‘) class B(A): def test(self): print(‘from B‘) class C(A): def test(self): print(‘from C‘) class D(B): def test(self): print(‘from D‘) class E(C): def test(self): print(‘from E‘) class F(D,E): # def test(self): # print(‘from F‘) pass print(F.mro()) #这个属性可以查看继承属性 #新式类继承顺序:F->D->B->E->C->A #经典类继承顺序:F->D->B->A->E->C #python3中统一都是新式类 #pyhon2中才分新式类与经典类
继承顺序
上面这个图这是让大家了解一下,平时可以直接使用mro()方法查看