抽象基类,即 Python 中的 abc 模块,Abstract Base Class。
Python 抽象基类可以看作是 Java 中的 接口 ( interface )
。 Java 不支持多重继承,但是可以继承多个接口,而且接口是不能被实例化的。因此,Python 的抽象基类也是不能被实例化的。
Python 作为一种动态语言,其变量是没有类型的,只是作为一种符号而已,可以指向任何类型的对象。因此,Python 从语言层面讲就已经支持多态了,并不需要像 Java 一样自己来实现多态了。
Python 之类的动态语言的缺陷在于无法做类型检查,所以说无法在编译时发现代码的错误,只能在运行时发现错误。
Python 信奉的是 鸭子类型,其和魔法函数构成了我们 Python 的协议。Python 不需要通过继承某个类或者接口才能具有某些特性,而只需要实现指定的魔法函数,我们的类就是某种类型的对象。不同的魔法函数赋予了类不同的特性。
比如我们实现了字符表示、迭代器等魔法函数之后,我们的对象就是某种指定的类型。这种特性在 Python 中被称为 协议。这种事先约定好的做法,我们都可以称之为一种协议。在编程的时候,需要遵循这些协议。
抽象基类是什么?
1.在基础类中,设定好一些方法,然后所有继承基类的类都必须覆盖抽象基类中的方法。
2.抽象基类无法被实例化的。
问题:Python 基于鸭子类型设计的,为什么又多出来一个 抽象基类 的概念呢?实现某些方法不就行了吗?
两种应用场景:
1.检查一个类是否有某个方法
# 检查某个类是否有某个方法
class Company:
def __init__(self, employee_lst):
self.employee = employee_lst
def __len__(self):
return len(self.employee)
com = Company(["Bob","Jack"])
# 判断是否有 __len__, Python 中 函数 也是一种 attribute。
print(hasattr(com, "__len__"))
print(len(com))
>>>
True
2
在某些情况下,判断某个对象的类型,需要抽象基类。
from collections.abc import Sized
isinstance(com, Sized)
2.强制一个子类必须实现某些方法 (框架实现中常用)
# 集成 cache,以后可以用 redis、memorycache等替换
# 这时候需要设定一个抽象基类,子类必须实现某些方法
class CacheBase:
def get(self, key):
#模拟抽象基类,可以设计为抛一个异常,但有不好的地方
raise NotImplementedError
def set(self, key, value):
raise NotImplementedError
class RedisCache(CacheBase):
pass
redis_cache = RedisCache()
redis_cache.set('title','Hello World!')
# 不去重写这些方法的话,运行时(调用的时候)才会抛出异常
Traceback (most recent call last):
File "/Users/tsw/Documents/CodeCampus/LearningPython/ch04/abc.py", line 16, in <module>
redis_cache.set('title','Hello World!')
File "/Users/tsw/Documents/CodeCampus/LearningPython/ch04/abc.py", line 8, in set
raise NotImplementedError
NotImplementedError
Python抽象基类如何实现?
# 全局下的 abc 模块,实现抽象基类
import abc
# metaclass 必须这样写
class CacheBase(metaclass = abc.ABCMeta):
# 加一个装饰器,就不用 raise 一个异常了
@abc.abstractmethod
def get(self, key):
pass
@abc.abstractmethod
def set(self, key, value):
pass
class RedisCache(CacheBase):
pass
redis_cache = RedisCache()
这时候调用的话,可见在其初始化的时候就已经发现问题,这算是 abc 给我们带来的好处。
Traceback (most recent call last):
File "/Users/tsw/Documents/CodeCampus/LearningPython/ch04/abc.py", line 25, in <module>
redis_cache = RedisCache()
TypeError: Can't instantiate abstract class RedisCache with abstract methods get, set
在 Python 中已经实现了通用的抽象基类,被放置在 collections.abc 模块中。
抽象基类并不推荐使用!实际用的比较少。