34、Python之面向对象:定义类不让实例化?不讲还是很讲武德

南宫理的日志录 2024-10-12 09:26:58
引言

前面的几篇文章已经把Python面向对象的三大特性:封装、继承、多态,都大概介绍了一番。当然,肯定是挂一漏万……

这里有必要再次提及一下关于这个Python系列的两个出发点,一个是“有趣”,一个是“有用”。所以,文章中,基本只是把我认为涉及到设计思想、实现原理、相对较为实用的部分介绍了一下,在我看来,这三点只要占据一点,就是有趣或者有用的。

废话说完,来引入一下,今天文章的主角,Python中的abc。这个abc,不是一个学科或者一门技术的基础入门的内容(abc)。而是Abstract Base Classes(抽象基类)的缩写。

如果有同学之前学习过Java或者C#,一定听过或者用过抽象类和接口。对应到Python中,就是这个abc了。

什么是abc

首先来了解一下什么是abc。引言中已经简单提到了,所谓abc,是抽象基类的概念,在Python中有一个对应的用于定义抽象基类的模块abc。

抽象基类是一种用于定义接口的方式,允许开发者在类的层次结构中,强制实现特定方法。主要用于定义接口,并确保字类实现这些接口方法。

首先看一下abc模块的定义,abc.py这个模块中只有189行代码,暂时我们要用到的有两个:ABC类和abstractmethod装饰器,这里把它们的定义放出来:

通过继承ABC类,我们可以定义一个抽象基类。通过abstractmethod装饰器修饰类中定义的方法,可以标识一个方法为抽象方法。

通过代码演示,如何定义一个抽象基类,还是以前面提到的打工人为例:

执行结果:

可以看到,尝试实例化DaGongRen这个类会报错。从错误提示看,不能实例化是因为DaGongRen是个抽象类且有一个抽象方法work。

感兴趣的同学可以尝试取消继承ABC类,或者把work()方法上的abstractmethod装饰器注释掉,发现都是可以实例化的。

所以,要想定义一个不能被实例化的类,需要满足两点:

1、继承ABC类。

2、至少定义一个使用abstractmethod装饰器标记的方法。

什么时候用abc

是能定义一个抽象基类了,但是,肯定立马有人产生疑问,我费劲儿定义一个不能实例化的抽象基类图啥?

要说图啥,可能有点像电影里面反派和正派的对话:

反派:你啥也得不到,还这么拼,图啥?

正派:图个心安!

开个玩笑~~

其实,从逻辑上来讲,前面几篇文章中,定义的DaGongRen都是不合理的。

打工人其实是一个抽象的概念,虽然人人都是打工人,但是,打工人如果不进行具体化,他打的是什么工?怎么打工?

所以,DaGongRen应该定义为抽象基类,且work方法应该被标记为抽象方法。

具体来说,需要使用抽象基类的场景注意有:

1、类似于Java强类型语言中定义接口的场景:为了协作,需要定义一个通用的接口,让各个子类遵循相同的协议。

2、强制实现:当希望子类在被实例化前必须实现特定方法时,可以使用抽象基类。

3、设计模式:比如模板方法等,抽象基类可以用于定义模板的骨架,并要求字类提供具体的实现。

所以,可以把DaGongRen这个抽象基类作为一个需要遵循的协议,任何类继承了该类,要想实例化,必须实现work()方法:

当然,如果方法是空实现的(方法体只有一行pass)也是可以实例化的,虽然那样做,似乎没有任何意义。

内置类的abc使用

其实,在Python内置类中,有不少使用abc的实例。

1、collections.abc模块

Python在collections.abc模块中定义了很多抽象基类,用于自定义实现各种集合类型(比如列表、字典、集合等),这些抽象基类定义了各种集合类型应该支持实现的方法。

2、numbers模块

numbers模块中定义了数值相关的抽象基类,比如:Number、Integral、Real和Complex等,用于自定义数值类型,确保它们遵循预期的接口。

3、io模块

在io模块中,IOBase是一个抽象基类,定义了文件和流的相关接口协议。

总结

本文简单介绍了Python中的抽象基类abc模块,并介绍了定义抽象基类的方法,以及需要使用抽象基类的场景,并列举了抽象基类在Python内置模块中的使用实例。

在下一篇文章中,我们将通过使用抽象基类自定义实现一个序列类型,来进一步使用抽象基类。

感谢您的拨冗阅读,如果这篇文章对您有所帮助,欢迎点赞、收藏。

0 阅读:1

南宫理的日志录

简介:深耕IT科技,探索技术与人文的交集