1.说在前头

"抽象基类"这个词可能听着比较"深奥",其实"基类"就是"父类","抽象"就是"假"的意思,

"抽象基类"就是"假父类."

2.对之前元类的一点补充

之前说过通过元类实例化类的语法是

变量名 = type("类名", ("继承的类",), {"属性名":"属性值"})

现在介绍另一种方法

class 类名(metaclass=元类名):
...

举个例子:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# __author__: kainhuck # 定义一个元类
class MyMetaClass(type):
def __new__(cls, name, bases, attrs):
'''
:param name: 类名
:param bases: 继承的基类
:param attrs: 拥有的属性 要求必须含有name属性 '''
name = attrs.get("name", None)
if name and not callable(name):
return type.__new__(cls, name, bases, attrs)
raise NotImplementedError("必须含有name属性") # 定义普通类
# class MyClassA(metaclass=MyMetaClass): # 报错,没有定义name属性
# pass # 定义普通类
# class MyClassB(metaclass=MyMetaClass): # 报错,没有定义name属性
# def name(self):
# pass #
# class MyClassC(metaclass=MyMetaClass): # 报错,没有定义name属性
# def __init__(self):
# self.name = "kainhuck" #
class MyClassD(metaclass=MyMetaClass): # 没有报错
name = "kainhuck"

3.鸭子类型

鸭子类型:如果一个东西看起来想一个鸭子,叫起来像一个鸭子,那么它大概就是一只鸭子.

在Python中有些时候我们需要一个有某个功能(比如说:鸭子叫)的对象,那我们可以通过判断这个对象是不是一只鸭子来检测是否满足我们的需求;但仔细想想这有些缺陷,因为我们真正需要的是鸭子叫这个方法,一个对象无论是不是鸭子只要他会像鸭子一样叫就可以啦.

这话可能有些绕,让我来举一个例子

有这么一个函数:

def func(something):
print(something[0])

这个函数会打印出参数的第一个元素,其实这里隐含着一个条件--参数支持下标索引.为了使代码完善我们应该对该函数做点修改.

方案一.

def func(something):
if isinstance(something, (list, tuple, set)): # 这些方法支持下标索引
print(something[0])
else:
print("Error")

方案一的缺点:

这样写就默认把something的类型限定了,拓展性很差

我们知道只要自定义的类实现了__getitem__方法就可以使其支持下标索引.

方案二.

def func(something):
if hasattr(something, '__getitem__'):
print(something[0])
else:
print("Error")

方案二的缺点:

并不是所有实现__getitem__的方法都可以支持下标索引,比如字典类型

这样似乎没有解决方案了..其实抽象基类就完美的解决了这问题

4.抽象基类

1. 抽象基类的定义:

由abc.ABCMeta这个元类实现的类就是抽象基类,如

class AbstractClass(metaclass=abc.ABCMeta):
pass

2. register方法

定义好的抽象基类通过register方法可以成为别的类的父类

举个例子:

import abc

# 定义一个抽象基类
class AbstractClass(metaclass=abc.ABCMeta):
pass # 定义一个普通类继承自object
class MyClass(object):
pass # 把我们定义的抽象基类注册为MyClass的父类
AbstractClass.register(MyClass)
mc = MyClass()
print(issubclass(MyClass, AbstractClass)) # 输出True
print(isinstance(mc, AbstractClass)) # 输出True # 将我们定义的抽象基类注册到系统定义的类
AbstractClass.register(list) print(isinstance([], AbstractClass)) # 输出True

说明一点:抽象基类虽然可以成为别的类的父类,但是别的类并不会继承抽象基类的方法和属性

3. 对前面例子的方案三实现

方案三.

from abc import ABCMeta

class MySequence(metaclass=ABCMeta):
pass MySequence.register(list) # 注册为列表的父类
MySequence.register(tuple) # 注册为元组的父类 '''
也可以自定义一个类,将MySequence注册为其父类
'''
def func(something):
if isinstance(something, AbstractClass): # AbstractClass的子类
print(something[0])
else:
print("Error")

4.__subclasshook__魔法方法

看过上面的例子你们肯定会觉得,给每个类都注册一遍抽象基类太麻烦了,没错Python的开发者也这么觉得,于是__subclasshook__这个方法出现了

几点说明:

  1. 该方法定义在抽象基类中
  2. 该方法必须定义为类方法
  3. 该方法有三个返回值
    1. True: 如果测试类被认为是子类
    2. False: 如果测试类不被认为是子类
    3. NotImplemented: 这个后面讲

定义一个抽象基类:

import abc

class AbstractDuck(metaclass=abc.ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
quack = getattr(subclass, 'quack', None) # 取出subclass的 quack 属性,如果不存在则返回 None
return callable(quack) # 返回quack是否可以调用(是否是个方法)

定义两个测试类

class Duck(object):
def quack(self):
pass class NotDuck(object):
quack = "foo"

判断是否是AbstractDuck的子类

print(issubclass(Duck, AbstractDuck))  # 输出 True
print(issubclass(NotDuck, AbstractDuck)) # 输出 False

注意:__subclasshook__方法的优先级大于register

举个例子解释NotImplemented返回值

In [6]: import abc                                                                                                                                   

In [7]: class AbstractDuck(metaclass=abc.ABCMeta):
...: @classmethod
...: def __subclasshook__(cls, subclass):
...: quack = getattr(subclass, 'quack', None) # 取出subclass的 quack 属性,如果不存在则返回 None
...: if callable(quack):
...: return True
...: return NotImplemented
...: In [8]: class Duck(object):
...: def quack(self):
...: pass
...: In [9]: class NotDuck(object):
...: quack = "foo"
...: In [10]: issubclass(NotDuck, AbstractDuck)
Out[10]: False In [11]: AbstractDuck.register(NotDuck)
Out[11]: __main__.NotDuck In [12]: issubclass(NotDuck, AbstractDuck)
Out[12]: True

Python中的抽象基类的更多相关文章

  1. Python高级主题:Python ABC(抽象基类)

    #抽象类实例 作用统一规范接口,降低使用复杂度.import abcclass Animal(metaclass = abc.ABCMeta): ##只能被继承,不能实例化,实例化会报错 @abc.a ...

  2. C++中的抽象基类示例

    抽象基类(abstract base class,ABC)例子:圆与椭圆.建立一个基类BaseEllipse,建立它的恋歌继承了Ellipse和Circle.ellipse.h #ifndef ELL ...

  3. guxh的python笔记七:抽象基类

    1,鸭子类型和白鹅类型 1.1,白鹅类型 白鹅类型对接口有明确定义,比如不可变序列(Sequence),需要实现__contains__,__iter__,__len__,__getitem__,__ ...

  4. 【Python】【元编程】【从协议到抽象基类】

    """class Vector2d: typecode = 'd' def __init__(self,x,y): self.__x = float(x) self.__ ...

  5. Fluent_Python_Part4面向对象,11-iface-abc,协议(接口),抽象基类

    第四部分第11章,接口:从协议到抽象基类(重点讲抽象基类) 接口就是实现特定角色的方法集合. 严格来说,协议是非正式的接口(只由文档约束),正式接口会施加限制(抽象基类对接口一致性的强制). 在Pyt ...

  6. 6、面向对象以及winform的简单运用(抽象基类与接口)

    抽象类与抽象方法 1.书写规范: 在类前面加上abstract关键字,就成为了抽象类:在一个方法前面加上abstract关键字,就成为了抽象方法(抽象方法不能有实现方法,直接在后面加分号) 例: ab ...

  7. OOP2(虚函数/抽象基类/访问控制与继承)

    通常情况下,如果我们不适用某个函数,则无需为该函数提供定义.但我们必须为每个虚函数都提供定义而不管它是否被用到了,这因为连编译器也无法确定到底会适用哪个虚函数 对虚函数的调用可能在运行时才被解析: 当 ...

  8. Python中的对象行为与特殊方法(二)类型检查与抽象基类

    类型检查 创建类的实例时,该实例的类型为类本身: class Foo(object): pass f = Foo() 要测试实例是否属于某个类,可以使用type()内置函数: >>> ...

  9. python面对对象编程---------6:抽象基类

    抽象基本类的几大特点: 1:要定义但是并不完整的实现所有方法 2:基本的意思是作为父类 3:父类需要明确表示出那些方法的特征,这样在写子类时更加简单明白 用抽象基本类的地方: 1:用作父类 2:用作检 ...

随机推荐

  1. ffmpeg 学习: 004-参考文档进行的开发

    背景 在学习ffmpeg时,由于文档老旧以及ffmpeg新旧版本对于一些api的改动,导致学习受阻. 本来可以直接下载老的库,使用老的源码进行学习,但本人觉得,一味地守旧并不是一种好的方法. ffmp ...

  2. C语言整理复习——指针

    指针是C的精华,不会指针就等于没学C.但指针又是C里最难理解的部分,所以特意写下这篇博客整理思路. 一.指针类型的声明 C的数据类型由整型.浮点型.字符型.布尔型.指针这几部分构成.前四种类型比较好理 ...

  3. Java数组去重的方法

    //第一种方式:最开始想到的是利用Set集合的不可重复性进行元素过滤 public static Object[] oneClear(Object[] arr){  Set set = new Has ...

  4. fuseki远程访问方法

    ./fuseki-server启动服务后,我们的服务只能是localhost访问,无法被其他人访问,那么 要怎么修改呢.很简单,把apche-jena-fuseki-3.10.0/run 下面的shi ...

  5. java文件的上传

    文件的上传和下载在web应用中是非常常用,也是非常有用的功能.  例如:发送电子邮件时可以同过上传附件发送文件,OA系统中可以通过上传文件来提交公文,社交网站通过上传图片来自定义头像等等.  例如:下 ...

  6. ​IntelliJ IDEA使用技巧—使用EasyCode插件一键生成代码04期

    在现如今的软件开发过程中,软件开发人员将很多的精力放在重复的编码中.特别是流行的MVC架构模式下,项目各个层次的功能更加独立,这也间接的造成了代码的相似度更高.因此需要寻找一种可以减少软件开发人员重复 ...

  7. C# 关于AD域的操作 (首博)

    前段时间(因为懒得找具体的时间了)公司说让系统可以进行对AD域的操作,包括创建用户.于是上网查资料,了解何为AD域.还不知道的这边请https://www.cnblogs.com/cnjavahome ...

  8. 基于Ambari的WebUI实现集群扩容案例

    基于Ambari的WebUI实现集群扩容案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.将HDP的服务托管给Ambari服务 1>.点击“Service Auto S ...

  9. UVA - 548 Tree(二叉树的递归遍历)

    题意:已知中序后序序列,求一个叶子到根路径上权和最小,如果多解,则叶子权值尽量小. 分析:已知中序后序建树,再dfs求从根到各叶子的权和比较大小 #include<cstdio> #inc ...

  10. Inception SQL审核注解

    Inception SQL审核注解 1.建表语句 建表语句检查项 表属性的检查项 这个表不存在 对于create table like,会检查like的老表是不是存在. 对于create table ...