面向对象02 继承
Posted lianzibing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象02 继承相关的知识,希望对你有一定的参考价值。
继承 :解决代码的重复
#继承语法 class 子类名(父类名):pass class A: pass class B(A): pass # A 父类 基类 超类 # B子类 派生类
子类可以使用父类中的 : 方法和静态变量
class Animal: def __init__(self,name): self.name = name def eat(self): print('%s is eating'%self.name) def drink(self): print('%s is drinking'%self.name) def sleep(self): print('%s is sleeping'%self.name) class Cat(Animal): def climb_tree(self): print('%s is climbing'%self.name) class Dog(Animal): def house_keep(self): print('%s house keeping'%self.name) 小白 = Cat('小白') # 先开辟空间,空间里有一个类指针-->指向Cat # 调用init,对象在自己的空间中找init没找到,到Cat类中找init也没找到, # 找父类Animal中的init 小白.eat()#小白 is eating 小白.climb_tree()#小白 is climbing 小黑 = Dog('小黑') 小黑.eat()#小黑 is eating
当子类和父类的方法重名的时候,我们只使用子类的方法,而不会去调用父类的方法了
class Animal: def __init__(self,name): self.name = name def eat(self): print('%s is eating'%self.name) def drink(self): print('%s is drinking'%self.name) def sleep(self): print('%s is sleeping'%self.name) class Cat(Animal): def eat(self): print('%s吃猫粮'%self.name) def climb_tree(self): print('%s is climbing'%self.name) 小白 = Cat('小白') 小白.eat()#小白吃猫粮
子类想要调用父类的方法的同时还想执行自己的同名方法
在子类的方法中调用父类的方法 :父类名.方法名(self)
class Animal: def __init__(self,name,food): self.name = name self.food = food self.blood = 100 self.waise = 100 def eat(self): print('%s is eating %s'%(self.name,self.food)) def drink(self): print('%s is drinking'%self.name) def sleep(self): print('%s is sleeping'%self.name) class Cat(Animal): def eat(self): self.blood += 100 Animal.eat(self) def climb_tree(self): print('%s is climbing'%self.name) self.drink() class Dog(Animal): def eat(self): self.waise += 100 Animal.eat(self) def house_keep(self): print('%s is keeping the house'%self.name) 小白 = Cat('小白','猫粮') 小黑 = Dog('小黑','狗粮') 小白.eat()#小白 is eating 猫粮 小黑.eat()#小黑 is eating 狗粮 print(小白.__dict__)#'name': '小白', 'food': '猫粮', 'blood': 200, 'waise': 100 print(小黑.__dict__)#'name': '小黑', 'food': '狗粮', 'blood': 100, 'waise': 200 #父类和子类方法的选择: # 子类的对象,如果去调用方法 # 永远优先调用自己的 # 如果自己有 用自己的 # 自己没有 用父类的 # 如果自己有 还想用父类的 : 直接在子类方法中调父类的方法 父类名.方法名(self)
单继承
# 单继承 # 调子类的 : 子类自己有的时候 # 调父类的 : 子类自己没有的时候 # 调子类和父类的 :子类父类都有,在子类中调用父类的 class D: def func(self): print('in D') class C(D):pass class A(C): def func(self): print('in A') class B(A):pass B().func()
多继承
- 有一些语言不支持多继承 比如 java
- python语言的特点 : 可以在面向对象中支持多继承
# 多继承 # 一个类有多个父类,在调用父类方法的时候,按照继承顺序,先继承的就先寻找 class D: def func(self): print('in D') class C(D):pass class A(C): def func(self): print('in A') class B(A):pass B().func()
object类 类祖宗
所有在python3当中的类都是继承object类的
class A:pass print(A.__bases__)#(<class 'object'>,) class C:pass class B(A,C):pass print(B.__bases__)#(<class '__main__.A'>, <class '__main__.C'>)只能找到父类 不能找到父类的父类
绑定方法和普通的函数
from types import FunctionType,MethodType # FunctionType : 函数 # MethodType : 方法 class A: def func(self): print('in func') print(A.func) # 函数 #<function A.func at 0x000002841C479488> a = A() print(a.func) # 方法 #<bound method A.func of <__main__.A object at 0x000002841C308240>> print(isinstance(a.func,FunctionType))#False print(isinstance(a.func,MethodType))#True print(isinstance(A.func,FunctionType))#True print(isinstance(A.func,MethodType))#False #类.func是函数 #对象.func是方法
pickel 可以序列化对象
前提py文件里有对象的类
class Course: def __init__(self,name,period,price): self.name = name self.period = period self.price = price python = Course('python','6 moneth',21800) linux = Course('linux','5 moneth',19800) go = Course('go','4 moneth',12800) import pickle with open('pickle_file','ab') as f: pickle.dump(linux,f) pickle.dump(go,f) with open('pickle_file','rb') as f: while True: try: obj = pickle.load(f) print(obj.name,obj.period) except EOFError: break
继承进阶
类 的继承
多继承
- 只要继承object类就是新式类
- 不继承object类的都是经典类
在python3中 所有的类都继承object类,都是新式类
在python2中 不继承object的类都是经典类,继承object类的就是新式类了
经典类:在py3中不存是,在py2中不主动继承object的类
在py2中
class A:pass #经典类
class B(object):pass#新式类
在py3中
class A: #pass #新式类
class B(object):pass #新式类
在单继承方面(无论是新式类还是经典类都是一样的)
class A:
def func(self):pass
class B(A):
def func(self):pass
class C(B):
def func(self):pass
class D(c):
def func(self):pass
d = D()
寻找某一个方法的顺序:D->C->B->A
越往父类走,是深度
多继承
class A:
def func(self):
print('A')
class B(A):
def func(self):
print('B')
class C(A):
def func(Self):
print(C)
class D(B,C):
def func(self):
print('B')
print(D.mro()) #只在新式类中有,经典类没有
d = D()
d.func()
在走到一个点,下一个点既可以深度走,也可以从广度走的时候,总是先走广度,再走深度,广度优先
在经典类总,都是深度优先,总是在一条路走不通之后再换一条路,走过的点不会再走了
C3算法
算法内容:
- 如果是单继承 那么总是按照从子类->父类的顺序来计算查找顺序
- 如果是多继承 需要按照自己本类,父类1的继承顺序,父类2的继承顺序
- merge的规则:如果一个类出现在从左到右所有顺序的最左侧,并没有在其他位置出现,那么先提出了作为继承顺序中的一个 或 一个类出现在从左到右顺序的最左侧,并没有在其他顺序出现那么先提出了作为继承顺序中的第一个
- 如果从左到右第一个顺序中的第一个类出现在后面且不是第一个,那么不能提取,顺序向后继续找其他顺序中父类上述条件的类
- super — 是按照mro顺序来寻找当前类的下一个父类的 (与mro方法的关系)
在python3中不需要传参数,自动就帮我们寻找当前类的mro顺序的下一个类中的同名方法
在python2中的新式类中,需要我们主动传递参数 super(子类的名字和子类的对象).函数名(),这样才能够帮我们调用到这个子类的mre顺序的下一个类中的方法
在python2中的经典类中,并不支持使用super来找下一个类
class A(object): def func(self):print('A') class B(A): def func(self): super().func() print('B') class C(A): def func(self): super().func() print('C') class D(B,C): def func(self): super().func() print('D') D().func() ''' A C B D '''
其中 super().func() 也可以写成 super(D,self).func() 。
在单继承的程序中,super就是找父类:
class User: def __init__(self,name): self.name = name class VIPUser(User): def __init__(self,name,level,strat_date,end_date): # User.__init__(self,name) # super().__init__(name) # 推荐的 # super(VIPUser,self).__init__(name) self.level = level self.strat_date = strat_date self.end_date = end_date ''' 解析:中间的# 的三种方式功能都是找到并执行父类中的__init__的方法,推荐使用 super().__init__(name)。 '''
先找每个类的继承关系(注意因为加载顺序的缘故从,上至下开始寻找) A(0) = [AO] # A 继承于object类 B(A) = [BAO] # B继承于A类 C(A) = [CAO] # C继承于A类 D(B) = [DBAO] # D继承于B类 E(C) = [ECAO] # E继承于C类 那么算法为: F(D,E) = merge(D(B)+E(C)) ? = [F] + [DBAO] + [ECAO] F = [DBAO] + [ECAO] FD = [BAO] + [ECAO] FDB = [AO] + [ECAO] FDBE = [AO] + [CAO] FDBEC = [AO] + [AO] FDBECAO - **使用子类名.mro() 查看继承顺序** 只有新式类中有,经典类没有的。
- 通过继承实现的类的开发规范
父类对子类的约束
普通类 如 class A B C等等
抽象类
- 是一个开发的规范,约束他的所有子类必须实现一些和他同名的方法
引入:例:不同付款方式付款
class Wechat(): def __init__(self,name): self.name = name def pay(self,money): print('%s通过微信支付%s钱成功'%(self.name,money)) class Alipay(): def __init__(self,name): self.name = name def pay(self,money): print('%s通过微信支付%s钱成功'%(self.name,money)) class Apple(Payment): def __init__(self,name): self.name = name def pay(self,money): dic = 'name': self.name, 'number': money # 想办法调用苹果支付 url连接 把dic传过去 print('%s通过苹果支付%s钱成功' % (self.name, money)) aw = WeChat('alex') aw.pay(400) aa = Alipay('alex') aa.pay(400) ''' 这样我们通过创建了2个对象,然后调用类中的方法pay,把钱传入完成支付的功能 '''
当然我们有时需要归一化设计:
def pay(name,price,kind): if kind == 'Wechat': obj = WeChat(name) elif kind == 'Alipay': obj = Alipay(name) elif kind == 'Apple': obj = Apple(name) obj.pay(price) pay('alex',400,'Wechat') pay('alex',400,'Alipay') pay('alex',400,'Apple') ''' 我们通过定义一个函数,通过判别传入的kind参数,选择创建实例化name对象,然后调用方法,完成付款。 '''
注意:完成上述需求需要三个类中的pay的方法名一样如果不一样就会报错。
为解决上述问题引入了抽象类对子类进行相关的约束
class Payment: def pay(self,money): raise NotImplementedError('请在子类中重写同名pay方法') class Alipay(Payment):pass class WeChat(Payment):pass class Apple(Payment): pass def pay(name,price,kind): if kind == 'Wechat': obj = WeChat(name) elif kind == 'Alipay': obj = Alipay(name) elif kind == 'Apple': obj = Apple(name) obj.pay(price) ''' 解析:我们创建一个所有类的父类,定义一个pay的方法,如果执行,则抛出异常。 当我们执行pay函数时,我们会调用方法 pay ,当对象中没有pay的方法时,就会去父类Payment中调用,所以抛出异常。所以加入抽象类约束我们子类中方法名称一致。 ''' # 另一种方式:依赖于abc 模块 from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): @abstractmethod def pay(self,money): '''只要你见到了项目中有这种类,你要知道你的子类中必须实现和pay同名的方法''' raise NotImplementedError('请在子类中重写同名pay方法') # 一旦子类中没有pay方法,实例化都无法完成。 方式特点:约束力强。缺点:依赖于abc模块
多态
python中处处是多态,一切皆对象
什么是多态,借助java讲解
一个类型表现出来的多种状态
在Java中:一个参数必须指定类型,所以如果想让俩个类型的对象都可以传,那么必须让这俩个类继承自一个父类,再指定类型的时候使用父类来指定
鸭子类型
class list: def __len__(self):pass class dict: def __len__(self): pass class set: def __len__(self): pass class tuple: def __len__(self): pass class str: def __len__(self): pass def len(obj): return obj.__len__() # 如上述所有实现了__len__方法的类,在调用len函数的时候,obj都说是鸭子类型。 # 再如:迭代器协议 __iter__ __next__ 是迭代器。即所有的迭代器都是鸭子类型
以上是关于面向对象02 继承的主要内容,如果未能解决你的问题,请参考以下文章