抽象基类不包含我们需要的所有方法,其他的类会将他当作基类来使用。
抽象基类为缺失的方法函数提供了签名,子类必须创建符合基类定义的接口。
抽象基类可以保证一组自定义类的一致性,可以用它创建一些通用、可重用的抽象。
抽象基类的核心定义在 collections.abc 中。
liskov原则: 程序中原本引用父类的代码,改为引用这个父类的子类,并不会造成英雄
内置对象使用的抽象基类
dict
MutableMapping 是 dict 的抽象基类 , 它是 Mapping 的扩展,但是没有具体内置的实现
list
Sequence 是 list 的抽象基类
tuple
MutableSequence 是 tuple 的抽象基类
set
Set 是 set 的抽象基类
frozenset
默认set是不允许插入set的,但是可以将fornzenset 插入到set中。
MutableSet 是 frozenset 的抽象基类
自定义一个抽象基类
import abc
class BaseTest(metaclass=abc.ABCMeta):
@abc.abstractmethod
def case_one(self):
pass
def echo(self):
print('....')
class Test(BaseTest):
def case_one(self):
return 1
# 被@abc.abstractmethod装饰的函数子类必须实现,否则会报错
# 没被装饰的函数为正常函数,可以重写,但不是必须的。
可调用对象
所有的函数都认为自己属于 abc.Callable的子类
from collections import abc
def test():
return 1
>> print(isinstance(test, abc.Callable))
>> True
正常来说,一个类的实例对象是无法直接调用的
class Power:
pass
p = Power()
p(1,2) # 这是无法调用的
# 而实现了call方法的类,是可以调用的
class Power:
def __call__(self,x,y):
return x+y
# 更好的方式是继承Callable对象,这样更利于代码的阅读以及打印详细的报错信息
class Power(collections.abc.Callable):
def __call__(self,x,y):
return x+y
记忆化与缓存
一个普通的函数是无法缓存上次的运行结果的,函数通常是无状态的。但是一个可调用对象是有状态的,自然可以缓存上次的运行结果
from collections import abc
class Power(abc.Callable):
def __init__(self):
self.memo = {} # 用于记录结果
def __call__(self, x, y):
if (x,y) in self.memo: # 如果曾经计算过则直接返回
return self.memo[x,y]
else:
r = x+y
self.memo[x,y] =r
return r
>> p = Power()
>> p(1+2)
>> 3
>> p.memo # 会记录下这次的运行结果
>> {(1, 2): 3}
简化API
一个例子
from collections import abc
class Power(abc.Callable):
"""
这是一个没有意义的类,仅仅是为了演示优化。
直接调用实例会返回调用次数, add 或 sub 设置属性时会对应的 加一或 减一
直接点用add 或 sub 会返回上一次设置的属性值.
"""
def __init__(self):
self.count = 0
self._add = 0
self._sub =0
@property
def add(self):
return self._add
@add.setter
def add(self,v):
self._add = v+1
self.count +=1
@property
def sub(self):
return self._sub
@sub.setter
def sub(self, v):
self._sub = v-1
self.count +=1
def __call__(self, x, y):
return self.count
上面的代码我们实际上关心的只有 setter , 只有设置的时候作相应的操作。
但是以特性的方式,必须要用 @property , 这样代码会比较臃肿,所以下面是优化后的。
from collections import abc
class Power(abc.Callable):
"""
这是一个没有意义的类,仅仅是为了演示优化。
直接调用实例会返回调用次数, add 或 sub 设置属性时会对应的 加一或 减一
直接点用add 或 sub 会返回上一次设置的属性值
"""
def __init__(self):
self.count = 0
self.add = 0
self.sub =0
def __setattr__(self, key, value):
if key == 'add':
value +=1
self.count += 1
elif key == 'sub':
self.count += 1
value -=1
super().__setattr__(key,value)
def __call__(self):
return self.count