python-抽象-接口-多态
抽象类
什么是抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。抽象类是将一堆的类中抽象抽取相同的内容,其其中包括了数据的属性和函数的属性。
python中实现的抽象的类
看下面的代码:
class Wechatpay:
def __init__(self, name, money):
self.name = name
self.money = money
def pay(self):
print('%s通过微信支付了%s元' % (self.name, self.money))
class Alipay:
def __init__(self, name, money):
self.name = name
self.money = money
def pay(self):
print('%s通过支付宝支付了%s元' % (self.name, self.money))
# 为了归一化设计
def pay(person): # 直接传入一个对象,对象调用pay方法 ,不管的alipay和Wechatpay
person.pay()
# 实列化对象
ali = Alipay("Tom", 10000)
wch = Alipay("Jack", 100)
pay(ali)
pay(wch)
上述这样调用是没有任何的问题,但是某天,为了需求,需要添加applepay的功能,但是在定义的applepay中
没有定义pay方法,而是定义成为中文的汉语拼音fuqian的方法如下所示:
class ApplePay:
def __init__(self, name, money):
self.name = name
self.money = money
def fuqian(self):
print('%s通过apple pay支付了%s元' % (self.name, self.money))
app = ApplePay("Tom", 1000)
app.fuqian()
上述通过ApplePay实例化的对象,调用的fuqian方法是没有任何的问题,当时为了归一化的设计,我们
在使用applyPay实例化的对象,将对象传入到pay函数中,则会出现下面的报错信息,没有pay的方法
pay(app) # AttributeError: 'ApplePay' object has no attribute 'pay'
那怎么样处理这样的问题?
处理只要的问题,只能从规范的程度进行处理,在定义函数的时候,就必须设置相同的函数名,就如上述中ApplePay中方法fuqian应该改成和Alipay和WechatPlay中的pay,怎样在进行归一化的设计的时候,就不会出现找不到该方法了。在python中是使用abc模块来进行规范的,具体的使用方法如下所示:
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta): # 抽象类
@abstractmethod # 如果我必须要实现pay方法,那么我需要给pay加一个装饰器
def pay(self):
pass # 创建的这个pay并没有内容,
# 之所以写一个pay是为了提醒所有子类你一定要实现一个pay方法
@abstractmethod
def back(self):
pass
class Wechatpay(Payment):
def __init__(self,name,money):
self.name = name
self.money = money
def pay(self):
print('%s通过微信支付了%s元'%(self.name,self.money))
class Alipay(Payment):
def __init__(self,name,money):
self.name = name
self.money = money
def pay(self):
print('%s通过支付宝支付了%s元'%(self.name,self.money))
class ApplePay(Payment):
def __init__(self, name, money):
self.name = name
self.money = money
def pay(self):
print('%s通过apple pay支付了%s元' % (self.name, self.money))
def back(self):
print('退款')
# 归一化设计
def pay(person):
person.pay()
ApplePay('alex',20000)
特别注意的是,在类Payment中被语法糖装饰的函数,如果没有在子类中进行创建实现,那么在子类的实例化的时候,就会导致报错。如下所示:
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta): # 抽象类
@abstractmethod # 如果我必须要实现pay方法,那么我需要给pay加一个装饰器
def pay(self):
pass # 创建的这个pay并没有内容,
# 之所以写一个pay是为了提醒所有子类你一定要实现一个pay方法
@abstractmethod
def back(self):
pass
class Wechatpay(Payment):
def __init__(self,name,money):
self.name = name
self.money = money
def pay(self):
print('%s通过微信支付了%s元'%(self.name,self.money))
class Alipay(Payment):
def __init__(self,name,money):
self.name = name
self.money = money
def pay(self):
print('%s通过支付宝支付了%s元'%(self.name,self.money))
class ApplePay(Payment):
def __init__(self, name, money):
self.name = name
self.money = money
# def pay(self):
# print('%s通过apple pay支付了%s元' % (self.name, self.money))
# def back(self):
# print('退款')
# 归一化设计
def pay(person):
person.pay()
ApplePay('alex',20000)
上述的代码中,在ApplePay将pay方法进行注释了,在进行ApplePay('alex',20000)实例化的时候,出现了下面的错误:
TypeError: Can't instantiate abstract class ApplePay with abstract methods back, pay
总结:
抽象类 :Payment这个类是一个抽象类
抽象类做什么事儿 : 约束所有的子类 必须实现被abstractmethod装饰的方法名
给我们的代码指定规范
特点 : 抽象类不能实例化,只是作为具体的类的规范
抽象类长什么样
class 类名(metaclass = 'ABCMeta'):
@abstractmethod
def 规定的方法名(self):pass
@abstractmethod
def 规定的方法名(self):pass
@abstractmethod
def 规定的方法名(self):pass
接口类
类只能单继承,所以抽象类 只能是所有的子类只有一个规范
java 当中没有多继承的类
接口 接口可以多继承
在python里没有接口的专用语法
我们只是通过类的多继承模仿接口的效果
from abc import ABCMeta,abstractmethod
class NormalAnnimal(metaclass=ABCMeta):
@abstractmethod
def eat(self):pass
@abstractmethod
def drink(self):pass
class FlyAnimal(metaclass=ABCMeta):
@abstractmethod
def fly(self):pass
class SwimAnimal(metaclass=ABCMeta):
@abstractmethod
def swim(self):pass
class WalkAnimal(metaclass=ABCMeta):
@abstractmethod
def walk(self):pass
和抽象类一样,基类多装饰的方法,子类中都必须去实现它,在java中,基类所定义的方法,通常只是定义,而不能实现,而在python中就没有那么对限制,可以去实现,但是一般也是只是定义方法,在子类中去实现。
总结:
- 抽象类 是单继承的规范
- 接口类 是多继承的规范
java中
- 接口里面定义的所有的方法 都不能写具体的实现 pass
- 抽象类里面定义的所有的抽象方法 内部是可以完成一些简单的代码
多态
多态指的是一类事物有多种形态
动物有多种形态:人,狗,猪
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
@abc.abstractmethod
def talk(self):
pass
class People(Animal): #动物的形态之一:人
def talk(self):
print('say hello')
class Dog(Animal): #动物的形态之二:狗
def talk(self):
print('say wangwang')
class Pig(Animal): #动物的形态之三:猪
def talk(self):
print('say aoao')
文件有多种形态:文本文件,可执行文件
import abc
class File(metaclass=abc.ABCMeta): #同一类事物:文件
@abc.abstractmethod
def click(self):
pass
class Text(File): #文件的形态之一:文本文件
def click(self):
print('open file')
class ExeFile(File): #文件的形态之二:可执行文件
def click(self):
print('execute file')
多态性
peo=People()
dog=Dog()
pig=Pig()
#peo、dog、pig都是动物,只要是动物肯定有talk方法
#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()
#更进一步,我们可以定义一个统一的接口来使用
def func(obj):
obj.talk()
鸭子类型
Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’
# 二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
def read(self):
pass
def write(self):
pass
class DiskFile:
def read(self):
pass
def write(self):
pass
总结:
# 鸭子类型
# python当中写程序的一种特殊的情况
# 其他语言中 正常的我们说一个数据类型具有某个特点,通常是通过继承来实现
# 继承迭代器类,来证明自己本身是个迭代器
# 继承可哈希的类,来证明自己本事是可哈希的
# 但是所有的这些都不是通过继承来完成的
# 我们只是通过一种潜规则的约定,如果具有__iter__,__next__就是迭代器
# 如果具有__hash__方法就是可哈希
# 如果具有__len__就是可以计算长度的
# 这样数据类型之间的关系并不仅仅是通过继承来约束的
# 而是通过约定俗成的关系来确认的
# 多态
# 在传递参数的时候,如果要传递的对象有可能是多个类的对象
# 我们又必须在语言中清楚的描述出到底是那一个类型的对象
# 我们就可以使用继承的形式,有一个父类作为这些所有可能被传递进来的对象的基类
# 基础类型就可以写成这个父类
# 于是所有子类的对象都是属于这个父类的
# 在python当中,因为要传递的对象的类型在定义阶段不需要明确,所以我们在python中处处都是多态
# 数据的类型不需要通过继承来维护统一