当前位置: 首页 > 后端技术 > Python

Python中的面向接口编程

时间:2023-03-25 21:23:33 Python

前言《面向接口编程》写Java的朋友已经可以闻茧了。当然这个思想在Java中非常重要,甚至几乎所有的编程语言都需要它。毕竟程序具有良好的扩展性和可维护性,谁也无法拒绝。最近刚开始写Python的时候无意中看到了部分代码。当时的实现需求有一个明显的特点:不同的对象具有共同的行为能力,但每个对象的具体实现方式不同。也就是说,商家需要接入平台,接入的步骤是一样的,只是具体实现方式不同。作为一个“资深”的Javaer,在看需求之前就把所有的实现类都写的洋洋洒洒的:当然,最后还是成功实现了需求,连群里一个没写过Java的大哥都愣了一下.称其为真棒。但是事后也跟我吐槽:你的设计不错,就是感觉很复杂。跟着代码走,要绕几圈才能找到真正的业务逻辑(实现类)。到目前为止,我已经写了很多Python,我终于可以总结一下他的感受:不够Pythonic。Python虽然没有Java那样的Interface特性,但是作为一种面向对象的高级语言,它也支持继承;这里我们也可以利用继承特性来实现面向接口的编程:run(self):print("bwmrun")defrun(car):car.run()if__name__=="__main__":benz=Benz()bmw=BMW()run(benz)run(bmw)的代码很简单,Python中也没有类似Java的extends关键字,只需要在类声明的末尾用圆括号把基类括起来就可以了。这样可以在每个子类中独立实现业务逻辑,方便扩展和维护。类型检查由于Python是一种动态类型的语言,因此无法像Java那样在编译时验证一个类是否完全实现了接口的所有方法。Python为此提供了一种解决方案,即abc(AbstractBaseClasses),当我们用abc声明基类时,我们可以近似:importabcclassCar(abc.ABC):@abc.abstractmethoddefrun(self):passclassBenz(Car):defrun(self):print("benzrun")classBMW(Car):passdefrun(car):car.run()if__name__=="__main__":benz=Benz()宝马=BMW()run(benz)run(bmw)一旦一个类没有实现某个方法,运行时就会抛出异常:bmw=BMW()TypeError:Can'tinstantiateabstractclassBMWwithabstractmethodsrun虽然不能done运行前要验证(毕竟不需要编译),但聊胜于无。以上两种ducktyping的方法,看似优雅地实现了面向接口编程,但实际上还不够Pythonic。在继续之前,让我们先谈谈界面的本质。Java等静态语言的面向接口编程比较麻烦,也就是我们常说的子类转化为父类,需要额外写代码。好处也很明显,只需要父类就可以运行。不过我们也不必太迷恋接口,它本身只是一种协议,一种规范,并没有特指Java中的Interface,甚至有些语言根本就没有这个关键字.动态语言的特性不需要强制验证方法是否实现。在Python中,我们可以使用鸭子类型来优雅地实现面向接口的编程。在此之前,先了解一下鸭子的种类,借用维基百科:“当你看到一只鸟,走路像鸭子,游泳像鸭子,叫声像鸭子,那么这只鸟就可以称为鸭子。”我用大白话翻译就是:即使两个根本不想做的类,如果都实现了同一个方法,也可以作为同类型的类。一个简单的例子:)create(order)create(user)这里的顺序和user本身没有关系,但是他们的方法都是一样的,受益于动态语言无法验证类型,所以可以在运行时考虑是同一类型。所以在duck类型的基础上,我们可以稍微简化一下前面的代码:print("bwmrun")defrun(car):car.run()if__name__=="__main__":benz=Benz()bmw=BMW()run(benz)run(bmw)因为在鸭子打字中我们关心关于它的行为,而不是它们的类型;所以面向接口的编程可以不用继承来实现。综上所述,我想从未接触过动态类型语言的朋友,了解了这些之后,就会发现新的世界,就像Python老手第一次使用Java时一样;虽然他们觉得语法啰嗦,但他们也会羡慕它的类型检查和参数验证。这样的功能。静态语言和口头语言的争论这里就不展开讨论了,各有千秋,鞋子好不好只有自己知道。顺便提一下,不仅动态语言有duck类型,一些静态语言也可以玩这个花哨的操作。如果大家有兴趣,下次再介绍。