Python 中的接口实现

发布于 2025-02-17 10:06:31 字数 3157 浏览 8 评论 0

在 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)

尽管在大多数情况下,通过协议实现接口都不会有什么问题,但在在有些情况下,通过协议或鸭子类型实现的非正式接口会导致一些问题。比如, CarPeople 都有 move 方法,即使通过协议定义的接口是一样的,但是他们的行为却完全不同。而 Python 内置的 ABCs(Abstract Base Classes)模块就可以帮我们解决这个问题。

ABCs 的使用非常简单,我们定义的基类在其本质上就是一个抽象类,而在基类上定义的方法就是抽象方法。所以任何从该基类派生出来的类,都要实现这些抽象方法。此时,我们就可以说这些派生出的类是接口(基类)的实现类。此时我们就可以使用类型检查,来检查类是否实现了特定的接口。

现在回到我们之前的 CarPeople 的例子,采用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

凉墨

暂无简介

文章
评论
26 人气
更多

推荐作者

朦胧时间

文章 0 评论 0

alipaysp_LqIz5u8my7

文章 0 评论 0

造雨人

文章 0 评论 0

树深时见影

文章 0 评论 0

无名指的心愿

文章 0 评论 0

Bonjour°[大白

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文