面向对象的三大特征
——继承,多态,封装
继承
继承是一种创建新类的方式,在python中新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
查看继承
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的父类
猫可以:喵喵叫、吃、喝、拉、撒 狗可以:汪汪叫、吃、喝、拉、撒 如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下: #继承的代码实现 class Animal: def eat(self): print("%s 吃 " %self.name) def drink(self): print ("%s 喝 " %self.name) def shit(self): print ("%s 拉 " %self.name) def pee(self): print ("%s 撒 " %self.name) class Cat(Animal): def __init__(self, name): self.name = name self.breed = '猫' def cry(self): print('喵喵叫') class Dog(Animal): def __init__(self, name): self.name = name self.breed='狗' def cry(self): print('汪汪叫') c1 = Cat('小白家的小黑猫') c1.eat() #输出结果为: #小白家的小黑猫 吃
子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类)
需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。(也就是重写父类方法)
所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法
新式类和经典类
# python2.7 新式类和经典类共存,新式类要继承object # python3中所有类都是新式类,python3中的类默认继承object ——新式类继承顺序广度优先 ——经典类继承顺序深度优先
# 经典类和新式类还有一个区别 mro方法只在新式类中存在
继承顺序问题
1 class F: 2 def func(self): 3 print('F') 4 class A(F): 5 def func(self): 6 print('A') 7 class B(A):pass 8 # def func(self): 9 # print('B') 10 class E(F): 11 def func(self): 12 print('E') 13 class C(E): 14 def func(self): 15 print('C') 16 class D(B,C): 17 pass 18 # def func(self):print('D') 19 20 d = D() 21 d.func() #输出的结果为:A 22 print(D.mro()) #打印D的继承关系
图一的继承顺序为:
D->B->A->C->E->F
图二的继承顺序为:
D->B->A->F->C->E
# 只要是子类的对象调用,子类中有的名字 一定用子类的,子类中没有才找父类的,如果父类也没有报错 # 如果父类 子类都有 用子类的 # 如果还想用父类的,单独调用父类的: # 父类名.方法名 需要自己传self参数 # super().方法名 不需要自己传self
super的使用
1 class Animal: 2 def __init__(self,name,aggr,hp): 3 self.name = name 4 self.aggr = aggr 5 self.hp = hp 6 def eat(self): 7 print('吃药回血') 8 self.hp+=100 9 10 class Dog(Animal): 11 def __init__(self,name,aggr,hp,kind): 12 super().__init__(name,aggr,hp) #<==>Animal.__init__(self,name,aggr,hp) 13 self.kind = kind # 派生属性 14 def eat(self):print('dog eating') 15 16 jin = Dog('狗',200,500,'teddy') 17 print(jin.name) 18 jin.eat() 19 super(Dog,jin).eat() 在外部使用super的时候需要传类和对象
super的查找顺序和继承顺序相同
#super 只在python3中存在
super的本质 :不是单纯找父类 而是根据调用者的节点位置的广度优先顺序来的
1 class A(): 2 def func(self): print('A') 3 4 class B(A): 5 def func(self): 6 super().func() 7 print('B') 8 9 class C(A): 10 def func(self): 11 super().func() 12 print('C') 13 14 class D(B,C): 15 def func(self): 16 super().func() 17 print('D') 18 #super的查找顺序和继承顺序相同 19 d = D() 20 d.func() #输出结果为:A C B D 21 print(D.mro()) #D->B->C->A
接口类
python中没有接口类,只是可以实现一个有接口类功能的类9(有抽象类)
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
1 class Alipay: 2 ''' 3 支付宝支付 4 ''' 5 def pay(self,money): 6 print('支付宝支付了%s元'%money) 7 8 class Applepay: 9 ''' 10 apple pay支付 11 ''' 12 def pay(self,money): 13 print('apple pay支付了%s元'%money) 14 15 16 def pay(payment,money): 17 ''' 18 支付函数,总体负责支付 19 对应支付的对象和要支付的金额 20 ''' 21 payment.pay(money) 22 23 24 p = Alipay() 25 pay(p,200) #支付宝支付了200元
接口类的多继承
1 #接口类的多继承 2 #tiger 走路 游泳 3 #swan 走路 游泳 飞 4 #oldying 走路 飞 5 from abc import abstractmethod,ABCMeta #1.引用这个模块 6 class Swim_Animal(metaclass=ABCMeta): #2.传入这个metaclass=ABCMeta 7 @abstractmethod #3.加上此装饰器的语法糖@abstractmethod 8 def swim(self):pass #满足这3点的就可以成为接口类 9 10 class Walk_Animal(metaclass=ABCMeta): 11 @abstractmethod 12 def walk(self):pass 13 14 class Fly_Animal(metaclass=ABCMeta): 15 @abstractmethod 16 def fly(self):pass 17 18 class Tiger(Walk_Animal,Swim_Animal): 19 def walk(self): 20 pass 21 def swim(self): 22 pass 23 24 class OldYing(Fly_Animal,Walk_Animal):pass 25 class Swan(Swim_Animal,Walk_Animal,Fly_Animal):pass
1 from abc import ABCMeta,abstractmethod 2 3 class Payment(metaclass=ABCMeta): 4 @abstractmethod 5 def pay(money): 6 print("支付了%s" % money) 7 8 class Wechatpay(Payment): 9 def fuqian(money): 10 print('微信支付了%s元'%money) 11 12 # p = Wechatpay(200) #此处实例化会报错 13 #只实例化一个类,不调用类中的方法就会报错 14 15 p = Wechatpay.fuqian(200) 16 p2 = Wechatpay.pay(100) 17 p3 = Payment.pay(50) 18 #执行结果: 19 #微信支付了200元 20 #支付了100 21 #支付了50
抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,
python支持抽象类,多继承。
抽象类可以有构造方法,接口不能有构造方法;
多态
多态指的是一类事物有多种形态
1 import abc 2 class File(metaclass=abc.ABCMeta): #同一类事物:文件 3 @abc.abstractmethod 4 def click(self): 5 pass 6 7 class Text(File): #文件的形态之一:文本文件 8 def click(self): 9 print('open file') 10 11 class ExeFile(File): #文件的形态之二:可执行文件 12 def click(self): 13 print('execute file') 14 15 多态动态绑定(在继承的背景下使用时,有时也称为多态性) 16 多态性是指在不考虑实例类型的情况下使用实例
鸭子类型
所谓鸭子类型,定义是‘是动态类型的一种风格‘。一个对象的特征不是由父类决定,而是通过对象的方法决定的。
封装
在python中用双下划线开头的方式将属性或者隐藏起来(设置成私有的)
1 class Person: 2 __key = 123 # 私有静态属性 3 def __init__(self,name,passwd): 4 self.name = name 5 self.__passwd = passwd # 私有属性 6 7 def __get_pwd(self): # 私有方法 8 return self.__passwd #只要在类的内部使用私有属性,就会自动的带上_类名 9 10 def login(self): # 正常的方法调用私有的方法 11 self.__get_pwd() 12 13 alex = Person('alex','alex3714') 14 print(alex._Person__passwd) # _类名__属性名(在内的外部只能通过这种方式调用私有属性和方法) 15 print(alex.get_pwd()) #此处的调用方法会报错
会用到私有的这个概念的场景
1.隐藏起一个属性 不想让类的外部调用
2.我想保护这个属性,不想让属性随意被改变
3.我想保护这个属性,不被子类继承