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

引言

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

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

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

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

什么是abc

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

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

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

579c04187d0b6499bb35b25f081056db.jpeg

a4d5393bde942f2b8a4677b20dcca698.jpeg

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

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

from abc import ABC, abstractmethod


class DaGongRen(ABC):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    @abstractmethod
    def work(self):
        pass


if __name__ == '__main__':
    # 尝试实例化DaGongRen
    dgr = DaGongRen('张三', 23)
    dgr.work()
    print(dgr)

执行结果:

e9afe7d3165ece7c1c1fac1d93dddd47.jpeg

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

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

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

1、继承ABC类。

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

什么时候用abc

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

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

反派:你啥也得不到,还这么拼,图啥?
正派:图个心安!

开个玩笑~~

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

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

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

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

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

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

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

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

from abc import ABC, abstractmethod


class DaGongRen(ABC):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    @abstractmethod
    def work(self):
        pass


class Programmer(DaGongRen):
    def work(self):
        print(f"{self.name}做的是编程的工作")


if __name__ == '__main__':
    dgr = Programmer('张三', 23)
    dgr.work()

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

内置类的abc使用

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

1、collections.abc模块

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

917e73d0c94fae44574296d6fc191154.jpeg

2、numbers模块

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

edc374d5d734fa747565221f10878405.jpeg

3、io模块

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

e73159f3ce358bfeb2382e98d136f7ef.jpeg

总结

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

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

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

  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南宫理的日知录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值