Python3 抽象基类
抽象基类
抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
抽象类与普通类的不同之处在于:抽象类中要有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。
疑问: 已经有了鸭子类型 和多态 ,为什么还要用这个呢?
答: 为了 解决两个事情
使用抽象基类的情况:
1.某些情况下希望判断某个对象的类型
2.强制子类必须全部实现抽象方法
3.用于接口设计
4.规范不同人的代码
Python中并没有提供抽象类与抽象方法,但是提供了内置模块abc和collections.abc来模拟实现抽象类
由abc.ABCMeta这个元类实现的类就是抽象基类,如
class BaseObj(metaclass=abc.ABCMeta): """基类""" @abc.abstractmethod def get(self, value): print(value) @abc.abstractmethod def set(self, key, value): print(key, value) class Dog(BaseObj): def get(self, value): print(value) return value def set(self, key, value): pass dog = Dog() print(dog.get('fe_cow')) # 输出结果: fe_cow fe_cow
继承,使Dog类成为BaseObj的子类,不使用继承,可以用下面两种方法。
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
看过上面的例子你们肯定会觉得,给每个类都注册一遍抽象基类太麻烦了,没错Python的开发者也这么觉得,于是__subclasshook__
这个方法出现了
几点说明:
- 该方法定义在抽象基类中
- 该方法必须定义为类方法
- 该方法有三个返回值
- True: 如果测试类被认为是子类
- False: 如果测试类不被认为是子类
- NotImplemented: 这个后面讲
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 class Duck(object): def quack(self): pass class NotDuck(object): quack = "foo" print(issubclass(NotDuck, AbstractDuck)) AbstractDuck.register(NotDuck) print(issubclass(NotDuck, AbstractDuck) ) print(isinstance(NotDuck,AbstractDuck)) print(issubclass(Duck, AbstractDuck)) print(isinstance(Duck,AbstractDuck)) 结果: False True False True False
示例代码中只是检查是不是子类。因为父类中也没有抽象方法,子类也没有实现,所以,isinstance值为false。
collection.abc模块常用的抽象基类:
__all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator", "AsyncGenerator", "Hashable", "Iterable", "Iterator", "Generator", "Reversible", "Sized", "Container", "Callable", "Collection", "Set", "MutableSet", "Mapping", "MutableMapping", "MappingView", "KeysView", "ItemsView", "ValuesView", "Sequence", "MutableSequence", "ByteString", ]
抽象类与接口
什么是接口
接口可以理解为自己给使用者来调用自己功能方法的入口。
为什么要用接口
(1)可以实现权限控制,比如可以通过接口做一下访问控制,可以允许或者拒绝调用者的一些操作。
(2)降低了使用者的使用难度,使用者只需要知道怎么调用即可,不需要知道里边的具体实现方法。
抽象类特点
1.规定继承类必须具有抽象基类指定的方法
2.抽象基类无法实例化
以上两个特点,主要用于接口设计
import abc class Interface(metaclass=abc.ABCMeta): # 定义接口Interface类来模仿接口的概念,python中没有interface关键字来定义一个接口。 @abc.abstractmethod def read(self): # 定接口函数read pass @abc.abstractmethod def write(self): # 定义接口函数write pass class Txt(Interface): # 文本,具体实现read和write def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(Interface): # 磁盘,具体实现read和write def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法')
抽象类与接口
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而
接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
接口与归一化设计
归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大地降低了使用
者的使用难度。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合。
定义一个抽象类的目的:就是为了可以规范不同人的代码
使用抽象类注意的问题:
- 抽象类中可以包含抽象方法,也可以包含具体方法
- 抽象类中可以有方法,也可以有属性
- 抽象类不能直接实例化
- 子类可以不实现所有的抽象方法,这时子类则不能实例化。
import abc class People(metaclass=abc.ABCMeta): # 定义一个抽象的方法 @abc.abstractmethod def eat(self): pass #定义一个抽象类的方法 @abc.abstractclassmethod def drink(cls): pass # 定义一个静态抽象方法 @abc.abstractstaticmethod def work(): pass