Python 中的接口实现
在 Java 中接口作为语法的一部分,是受严格定义的。但在 Python 中,情况却非如此。
非正式接口的实现方式:协议/鸭子类型
与 Java 不同,Python 中没有 interface
关键字来定义一个接口。在动态语言(如 Python、JavaScript)中,很多事情都是隐式的。我们更多关注的是对象的行为,而非它的类型。
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
如果我们有一个对象,它即可以像鸭子一样走路,又可以像鸭子一样嘎嘎叫,那么我们就认为这个对象是一只鸭子。这就是所谓的“ 鸭子类型(Duck typing) ”。在运行时,我们是通过调用期望的方法来替代类型检查。当方法的调用行为符合我们的预期,那么就照常往后执行。否则,我们就认为程序出现了问题。为了安全起见,我们通常使用 try...except
代码块或使用 hasattr
来检查对象是否拥有某个方法。
在 Python 中,不论是什么类型的对象,都可以通过实现预期的方法来遵循特定的接口。这种非正式(非强制性)的接口定义被称为 协议(protocol) 。这样的接口通常由文档进行说明,或由约定俗成的方式进行定义。
class Zoo: def __init__(self, animals): self.__animals = animals def __len__(self): return len(self.__animals) def __contains__(self, animal): return animal in self.__animals zoo = Zoo(['Tiger', 'Elephant', 'Giraffe']) # Sized protocol print(len(zoo)) # Container protocol print('Giraffe' in zoo) print('Monkey' in zoo) print('Monkey' not in zoo)
在我们上面的例子中,实现了 __len__
和 __contains__
方法,所以我们可以直接对 Zoo
实例使用 len
方法,使用 in
操作符检查成员。如果我们添加了 __iter__
方法来实现迭代器协议(iterable protocol),那么我们还可以这样做:
for animal in zoo: print(animal)
但在还没有实现 __iter__
方法前,我们尝试上面的迭代,将会得到以下错误:
TypeError: 'Zoo' object is not iterable
所以我们可以看到协议就像接口一样,可以通过实现协议所期望的方法来实现协议。
正式接口的实现方式:Abstract Base Classes(abc)
尽管在大多数情况下,通过协议实现接口都不会有什么问题,但在在有些情况下,通过协议或鸭子类型实现的非正式接口会导致一些问题。比如, Car
和 People
都有 move
方法,即使通过协议定义的接口是一样的,但是他们的行为却完全不同。而 Python 内置的 ABCs(Abstract Base Classes)模块就可以帮我们解决这个问题。
ABCs 的使用非常简单,我们定义的基类在其本质上就是一个抽象类,而在基类上定义的方法就是抽象方法。所以任何从该基类派生出来的类,都要实现这些抽象方法。此时,我们就可以说这些派生出的类是接口(基类)的实现类。此时我们就可以使用类型检查,来检查类是否实现了特定的接口。
现在回到我们之前的 Car
和 People
的例子,采用 ABCs 模块实现接口:
import abc class Car(abc.ABC): @abc.abstractmethod def move(self): pass class People(metaclass=abc.ABCMeta): @abc.abstractmethod def move(self): pass class Benz(Car): def move(self): print('奔驰嘟嘟嘟') class Chinese(People): def move(self): print('昂首挺胸嗒嗒嗒') @Car.register class BMW: pass benz = Benz() benz.move() chinese = Chinese() chinese.move() print(f'奔驰是汽车吗?{isinstance(benz, Car)}') print(f'中国人是汽车吗?{isinstance(chinese, Car)}') print(f'奔驰是人类吗?{isinstance(benz, People)}') print(f'中国人是人类吗?{isinstance(chinese, People)}') bmw = BMW() print(f'宝马是汽车吗?{issubclass(BMW, Car)}')
参考链接
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论