先来看一个例子:如果我们要写一个支付接口,怎么做呢
版本一:
class QQpay: def pay(self, money): print("此次消费%s" % money) class Alipay: def pay(self, money): print("此次消费%s" % money) a1 = QQpay() a2 = Alipay() a1.pay(100) a2.pay(200)
版本一虽然完成了支付功能,但是不同的支付没有统一,使用QQ支付时调用的是QQpay类的pay方法,使用Alipay支付的时候又变成调用Alipay类的pay方法,有没有办法使之统一呢,来看版本二。
版本二:
class QQpay: def pay(self, money): print("此次消费%s" % money) class Alipay: def pay(self, money): print("此次消费%s" % money)
def pay(obj, money): # 统一规范 obj.pay(money) a1 = QQpay() a2 = Alipay() pay(a1, 100) pay(a2, 200)
版本二通过在类外面定义了一个pay方法,然后通过pay方法去调用QQpay类和Alipay类的方法,这样就统一了接口,但是这里有一个问题,如果程序之后遇到升级,需要新加入一个微信支付的功能,但是接手这个项目的人这样写........
class Alipay: def pay(self, money): print("此次消费%s" % money) class Wechatpay: # 野生程序员写的 def zhifu(self, money): print("此次消费%s" % money) def pay(obj, money): # 统一规范 obj.pay(money) a1 = QQpay() a2 = Alipay() a3 = Wechat() pay(a1, 100) pay(a2, 200) pay(a3, 300) # 报错
很明显,执行pay(a3, 300)时会报错,因为Wechatpay里面并没有pay方法。我们当然可以通过事后更改代码的方法来解决这个问题,但是有没有从程序设计的层面上解决这个问题的方法呢,答案是有的
版本三:
class Pay: # 定义一个基类Pay,所有支付的类都继承这个类 def pay(self, money): pass class QQpay(Pay): def pay(self, money): print("此次消费%s" % money) class Alipay(Pay): def pay(self, money): print("此次消费%s" % money) class Wechatpay(Pay): def pay(self, money): print("此次消费%s" % money) def pay(obj, money): obj.pay(money) a1 = QQpay() a2 = Wechatpay() a3 = Alipay() pay(a1, 100) pay(a2, 200) pay(a3, 300)
版本三解决了支付名字不统一带来的报错问题,但是如果接手的程序员不按规定在自己写的类里定义pay方法,程序虽然不会报错但也不会正常支付,确切地说,版本三只是弱约束,正经的程序员看到了定义了一个基类Pay,其他支付的类都继承了Pay,就会知道这个Pay是统一接口用的,在自己的类里要定义一个pay方法,但是有些程序员比较有个性,就要取别的名字,这时版本三就无能无力了,因此我们对版本三进行了升级
版本四:
class Pay: def pay(self, money): raise NotImplementedError("未定义pay方法") # 抛出异常 class QQpay(Pay): # 正规写法 def pay(self, money): print("此次消费%s" % money) class Alipay(Pay): def pay(self, money): print("此次消费%s" % money) class Wechatpay(Pay): # 野生写法 def pay(self, money): print("此次消费%s" % money) class Unionpay(Pay): def zhifu(self, money): print("此次消费%s" % money) def pay(obj, money): obj.pay(money) a1 = QQpay() a2 = Wechatpay() a3 = Alipay() a4 = Unionpay() pay(a1, 100) pay(a2, 200) pay(a3, 300) pay(a4, 400) # NotImplementedError: 未定义pay方法
这里我们在基类的pay方法里面加上 raise Exception("未定义pay方法"),当执行pay(a4, 400)时,会先在Unionpay里面找,很显然找不到,之后会在其父类里面找,然后执行父类的pay方法,这时就会执行raise Exception("未定义pay方法"),这句话的意思是,抛出异常,异常的名字是Exception,异常的内容是"未定义pay方法"。版本四的核心就是在父类里定义一个pay方法,方法里只有抛出错误的语句,各子类继承父类,子类里必须定义名字相同的pay方法,否则执行时就会报错。还有一种从Java和C#继承过来的方法:运用抽象类(接口类)来解决
版本五:
from abc import ABCMeta, abstractmethod class Pay(metaclass=ABCMeta): # 这个父类定义了一个约束、规范,规定子类一定要有pay方法 """ 抽象类(接口类),制定一个规范,强制执行 """ @abstractmethod def pay(self, money): pass class Alipay(Pay): def pay(self, money): print("此次消费%s" % money) class Wechatpay(Pay): def zhifu(self, money): print("此次消费%s" % money) def pay(obj, money): obj.pay(money) a1 = Alipay() a2 = Wechatpay() # TypeError: Can't instantiate abstract class Wechatpay with abstract methods pay pay(a1, 100) pay(a2, 200)
总结:
1. 约束就是父类对子类的约束,子类必须要写XXX方法
2. 在python中实现约束有两种方法
(1)人为抛出异常的方法,并且尽量抛出的是NotImplementedError. 这样比较专业, ⽽且错误比较明确.(推荐)
(2)使用抽象类和抽象方法,由于该方案来源于Java和C#,所以还是用的比较少