Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Python抽象基类的定义与使用

Python抽象基类的定义与使用

作者头像
dongfanger
发布于 2021-10-20 09:01:28
发布于 2021-10-20 09:01:28
2.3K00
代码可运行
举报
文章被收录于专栏:dongfangerdongfanger
运行总次数:0
代码可运行

我们写Python基本不需要自己创建抽象基类,而是通过鸭子类型来解决大部分问题。《流畅的Python》作者使用了15年Python,但只在项目中创建过一个抽象基类。我们更多时候是创建现有抽象基类的子类,或者使用现有的抽象基类注册。本文的意义在于,了解抽象基类的定义与使用,可以帮助我们理解抽象基类是如何实现的,为我们以后学习后端语言(比如Java、Golang)打下基础。毕竟抽象基类是编程语言通用设计。

定义抽象基类的子类

先回顾下什么是抽象基类:Python的抽象基类是指必须让继承它的子类去实现它所要求的抽象方法的类。如下代码定义了抽象基类collections.MutableSequence的子类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import collections

Card = collections.namedtuple('Card', ['rank', 'suit'])

class FrenchDeck2(collections.MutableSequence):
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, position):
        return self._cards[position]

    def __setitem__(self, position, value):  # <1>
        self._cards[position] = value

    def __delitem__(self, position):  # <2>
        del self._cards[position]

    def insert(self, position, value):  # <3>
        self._cards.insert(position, value)

通过抽象基类collections.MutableSequence源码:

可以发现,它有三个抽象方法__setitem____delitem__insert,所以FrenchDeck2类必须实现它们。而对于其他非抽象方法比如appendextendpop等,则可以直接继承无需实现。

注意,Python只会在运行时实例化FrenchDeck2类时真正检查抽象方法的实现,如果未实现会抛出TypeError异常,提示Can't instantiate abstract class之类的。

标准库中的抽象基类

为了知道哪些抽象基类可以使用,我们可以看看标准库。

collections.abc

collections.abc的抽象基类如下图所示:

Iterable、Container、Sized

这三个抽象基类是最基础的类,各个集合都继承了这三个抽象基类。

  • Itearble通过__iter__方法支持迭代
  • Container通过__contains__方法支持in运算符
  • Sized通过__len__方法支持len()函数

Sequence、Mapping、Set

不可变集合类型,各自都有可变的子类。

MappingView

.items().keys().values()返回的对象分别是ItemsView、KeysView和ValuesView的实例。

Callable、Hashable

为内置函数isinstance提供支持,判断对象能不能调用或散列。

Iterator

迭代器。

numbers

numbers的抽象基类有以下几种:

  • Number
  • Complex
  • Real
  • Rational
  • Integral

这叫做数字塔,顶部是超类,底部是子类。比如使用isinstance(x, numbers.Integral)检查一个数是不是整数,这样代码就能接受int、bool(int的子类),再比如使用isinstance(x, numbers.Real)检查浮点数,这样代码就能接受bool、int、float、fractions.Fraction。

定义抽象基类

本小结可以跳过。不过了解抽象基类的定义有助于阅读标准库和其他包中的抽象基类源码。

抽象基类的示例代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# BEGIN TOMBOLA_ABC

import abc

class Tombola(abc.ABC):  # <1>

    @abc.abstractmethod
    def load(self, iterable):  # <2>
        """Add items from an iterable."""

    @abc.abstractmethod
    def pick(self):  # <3>
        """Remove item at random, returning it.
        This method should raise `LookupError` when the instance is empty.
        """

    def loaded(self):  # <4>
        """Return `True` if there's at least 1 item, `False` otherwise."""
        return bool(self.inspect())  # <5>


    def inspect(self):
        """Return a sorted tuple with the items currently inside."""
        items = []
        while True:  # <6>
            try:
                items.append(self.pick())
            except LookupError:
                break
        self.load(items)  # <7>
        return tuple(sorted(items))


# END TOMBOLA_ABC

要点:

  1. 继承abc.ABC
  2. 使用@abc.abstractmethod装饰器标记抽象方法
  3. 抽象基类也可以包含普通方法
  4. 抽象基类的子类必须覆盖抽象方法(普通方法可以不覆盖),可以使用super()函数调用抽象方法,为它添加功能,而不是从头开始实现

再看白鹅类型

白鹅类型的定义有一点难以理解,如果理解了虚拟子类,就能加快理解白鹅类型。虚拟子类并不是抽象基类的真正子类,而是注册到抽象基类上的子类,这样Python就不会做强制检查了。

注册的方式有两种:

register方法

Python3.3以前只能使用register方法,比如collections.abc模块的源码中,把内置类型tuple、str、range和memoryview注册为Sequence的虚拟子类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Sequence.register(tuple)
Sequence.register(str)
Sequence.register(range)
Sequence.register(memoryview)

register装饰器

把TomboList注册为Tombola的虚拟子类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Tombola.register
class TomboList(list):
    ...

白鹅类型和鸭子类型是Python的动态特性,它们的共同点是,只要长的像,Python就不会做强制检查,鸭子类型是针对普通类的子类而言的,白鹅类型是针对抽象基类的虚拟子类而言的。

参考资料: 《流畅的Python》第11章 接口:从协议到抽象基类

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-10-17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
流畅的 Python 第二版(GPT 重译)(七)
面向对象编程关乎接口。在 Python 中理解类型的最佳方法是了解它提供的方法——即其接口——如 “类型由支持的操作定义”(第八章)中所讨论的。
ApacheCN_飞龙
2024/03/21
2980
流畅的 Python 第二版(GPT 重译)(七)
Python中的接口协议和抽象基类
Python语言是没有interface关键字的,这也是动态类型语言的特点之一。Python的接口指的是类实现或继承的公开属性,包括数据或方法。比如Sequence的正式接口如下图所示:
dongfanger
2021/09/28
2.1K0
Python之抽象基类建议收藏
  以上为Animal的抽象基类,注意重写了__subclasscheck__(cls, subclass)方法来改变issubclass或者isinstance的行为,__subclasscheck__(cls, subclass)必须为@classmethod
全栈程序员站长
2022/07/14
7050
Python中的协议 、鸭子类型 、 抽象基类 、混入类
本篇文章探讨一下python中的几个概念:协议 、鸭子类型 、 抽象基类 、混入类。
Python中文社区
2018/07/26
2.1K0
Python中的协议 、鸭子类型 、 抽象基类 、混入类
Python 模块:abc
模块abc提供了在 Python 中定义 抽象基类 (ABC) 的组件,在 PEP 3119 中已有概述。查看 PEP 文档了解为什么需要在 Python 中增加这个模块。(也可查看 PEP 3141 以及 numbers 模块了解基于 ABC 的数字类型继承关系。)
不可言诉的深渊
2019/07/26
1.3K0
python的抽象基类
与jvm上的语言不一样,python的语言没有interface关键字,而且除了抽象基类,每个类都有相应的接口:类实现或继承的公开属性(方法或数据类型)
哒呵呵
2018/08/06
9940
第二章、深入类和对象
2.1.鸭子类型和多态 “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”  我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。 实例一: # 鸭子类型和多态简单实例 class Dog(object): def say(self): print('a dog') class Cat(object): def say(self): print('a cat') class Duck(object):
zhang_derek
2018/05/30
9350
python抽象基类abc
python中并没有提供抽象类与抽象方法,但是提供了内置模块abc(abstract base class)来模拟实现抽象类。
用户2936342
2018/08/27
9120
Python的abc模块XXX
该模块提供了在 Python 中定义 抽象基类 (ABC) 的组件,在 PEP 3119 中已有概述。查看 PEP 文档了解为什么需要在 Python 中增加这个模块。(也可查看 PEP 3141 以及 numbers 模块了解基于 ABC 的数字类型继承关系。)
狼啸风云
2022/04/01
9710
python中如何自定义序列类
+=又叫就地加,是通过一个模板函数来实现的(可以参考抽象基类中的可变序列类型MutableSequence)
make a bug
2022/09/20
9300
Python 函数如何实现“重载”
假如你想在交互模式下打印出美观的对象,那么标准库中的 pprint.pprint() 函数或许是一个不错的选择。但是,如果你想 DIY 一个自己看着舒服的打印模式,那么你很可能会写一长串的 if/else 语句,来判断传进来对象的类型。
用户2870857
2019/12/23
8700
python 列表的实现探析
知其然也要知其所以然,python中的容器对象真的不多,平常我们会很心安理得的根据需求来使用对应的容器,不定长数据用list,想去重用set,想快速进行匹配用dict,字符处理用str,可为何能实现这个效果呢?比如我们用list的时候,知道这玩意可以随意存储各种格式,存整型、浮点、字符串、甚至还可以嵌套list等其他容器,这底层的原理到底是用数组实现的,还是用链表?比如我们的字典,底层是用数组还是其他?如果是其他如哈希表,那又怎么实现输入数据的顺序排列?这次不妨一层层剖析,推演一番。贪多嚼不烂,本次就先对list进行分析
Yerik
2021/05/01
1.9K0
python高级编程第一讲:深入类和对象
多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚"鸭子类型"
小海怪的互联网
2019/08/23
6620
python高级编程第一讲:深入类和对象
《流畅的Python》第十一章学习笔记
协议是接口,但不是正式的,因此协议不能像正式接口那样施加限制。一个类可能只实现部分接口。
zx钟
2021/01/07
4870
【python高级编程】namedtuple用法--给元组中的每个元素命名
给每个元组中的元素命名,我们就可以使用名字去访问对应元素,相对于索引访问,这样可以大大提高程序的可读性。
用户7886150
2020/12/24
3.1K0
python 魔术方法(一) 自定义容器类与类属性控制
此前的文章中,我们介绍了 Python 面向对象编程及对象的继承和派生。 接下来的几篇文章,我们将详细介绍 Python 解释器提供的一系列特殊方法 -- 魔术方法。
用户3147702
2022/06/27
6910
python 魔术方法(一) 自定义容器类与类属性控制
啃书---流畅的python : 第一章
假设k个维度dim_1,dim_2,dim_3....dim_k ; 每个维度的可能取值集合分别是V_1,V_2,...V_k 假设维度$dim_i$的当前值为v_i \in V_i ,则将这k个维度的组合映射到一个唯一值的公式可以表示为:
逸澄
2025/01/26
740
python 数据模型
(1) repr所返回的字符串应该准确(%r),无歧义,并且尽可能表达出如何用代码创建出这个被创建的对象,如repr(v);输出Vector(3, 4)
python亦希
2022/01/20
5140
孤陋寡闻了,Python中类竟可以像列表一样操作
如何让一个类A也可以以列表的方式操作,通过len(A)获取长度,通过A[1]获取1坐标的值
读懂原理
2022/12/18
2660
孤陋寡闻了,Python中类竟可以像列表一样操作
Python 抽象基类 ABC :从实践到优雅
今天我们来聊聊 Python 中的抽象基类(Abstract Base Class,简称 ABC)。虽然这个概念在 Python 中已经存在很久了,但在日常开发中,很多人可能用得并不多,或者用得不够优雅。
Piper破壳
2024/12/25
1740
相关推荐
流畅的 Python 第二版(GPT 重译)(七)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验