乐趣区

关于python:Python-中的面向接口编程

前言

”面向接口编程“Java 的敌人耳朵曾经能够听出干茧了吧,当然这个思维在 Java 中十分重要,甚至简直所有的编程语言都须要,毕竟程序具备良好的扩展性、维护性谁都不能回绝。

最近无意间看到了我刚开始写 Python 时的局部代码,过后实现的需要有个很显著的特点:

  • 不同对象具备公共的行为能力,但具体每个对象的实现形式又各不相同。

说人话就是商户须要接入平台,接入的步骤雷同,但具体实现不同。

作为一个”资深“Javaer,需要还没看完我就洋洋洒洒的把各个实现类写好了:

当然最终也顺利实现需要,甚至把组里一个没写过 Java 的大哥唬的一愣一愣的,直呼牛逼。

不过预先也给我吐槽:

  • 你这设计是不错,然而感觉好简单,跟代码时要找到真正的业务逻辑(实现类)得绕几圈。

截止目前 Python 写多了,我总算是能总结他的感触:就是不够 Pythonic

虽说 Python 没有相似 Java 这样的 Interface 个性,但作为面向对象的高级语言也是反对继承的;

在这里咱们也能够利用继承的个性来实现面向接口编程:

class Car:
    def run(self):
        pass

class Benz(Car):
    def run(self):
        print("benz run")

class BMW(Car):

    def run(self):
        print("bwm run")

def run(car):
    car.run()

if __name__ == "__main__":
    benz = Benz()
    bmw = BMW()

    run(benz)
    run(bmw)

代码非常简单,在 Python 中也没有相似于 Java 中的 extends 关键字,只须要在类申明开端用括号蕴含基类即可。

这样在每个子类中就能独自实现业务逻辑,不便扩大和保护。

类型查看

因为 Python 作为一个动静类型语言,无奈做到 Java 那样在编译期间校验一个类是否齐全实现了某个接口的所有办法。

为此 Python 提供了解决办法,那就是 abc(Abstract Base Classes),当咱们将基类用 abc 申明时就能近似做到:

import abc
class Car(abc.ABC):
    @abc.abstractmethod
    def run(self):
        pass

class Benz(Car):
    def run(self):
        print("benz run")

class BMW(Car):
    pass

def run(car):
    car.run()

if __name__ == "__main__":
    benz = Benz()
    bmw = BMW()

    run(benz)
    run(bmw)

一旦有类没有实现办法时,运行期间便会抛出异样:

bmw = BMW()
TypeError: Can't instantiate abstract class BMW with abstract methods run

尽管无奈做到在运行之前(毕竟不须要编译)进行校验,但有总比没有好。

鸭子类型

以上两种形式看似曾经毕竟优雅的实现面向接口编程了,但实际上也不够 Pythonic

在持续之前咱们先聊聊 接口 的实质到底是什么?

Java 这类动态语言中面向接口编程是比拟麻烦的,也就是咱们常说的子类向父类转型,因而须要编写额定的代码。

带来的益处也是不言而喻,只须要父类便可运行。

但咱们也不用过于执着于接口,它自身只是一个协定、标准,并不特指 Java 中的 Interface,甚至有些语言压根没有这个关键字。

动静语言的个性也不须要强制校验是否实现了办法。

Python 中咱们能够利用鸭子类型来优雅的实现面向接口编程。

在这之前先理解下鸭子类型,借用维基百科的说法:

  • “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就能够被称为鸭子。”

我用大白话翻译下就是:

即使两个齐全不想干的类,如果他们都实现了雷同的办法,那就能够把他们当做同一类型的类来应用。

举个简略例子:

class Order:
    def create(self):
        pass

class User:
    def create(self):
        pass

def create(obj):
    obj.create()

if __name__ == "__main__":
    order = Order()
    user = User()
    create(order)
    create(user)

这里的 orderuser 自身齐全没有关系,只是他们都有雷同办法,又得益于动静语言没法校验类型的特点,所以齐全能够在运行的时候认为他们是同一种类型。

因而基于鸭子类型,之前的代码咱们能够稍作简化:

class Car:
    def run(self):
        pass

class Benz:
    def run(self):
        print("benz run")

class BMW:
    def run(self):
        print("bwm run")

def run(car):
    car.run()

if __name__ == "__main__":
    benz = Benz()
    bmw = BMW()

    run(benz)
    run(bmw)

因为在鸭子类型中咱们在意的是它的行为,而不是他们的类型;所以齐全能够不必继承便能够实现面向接口编程。

总结

我感觉平时没有接触过动静类型语言的敌人,在理解完这些之后会发现新大陆,就像是 Python 新手第一次应用 Java 时;尽管感觉语法啰嗦,但也会艳羡它的类型查看、参数验证这类特点。

动静语言之争这里不做探讨了,各有各的好,鞋好不好穿只有本人晓得。

轻易提一下其实不止动静语言具备鸭子类型,有些动态语言也能玩这个骚操作,感兴趣下次再介绍。

退出移动版