抽象基类

抽象基类不包含我们需要的所有方法,其他的类会将他当作基类来使用。

抽象基类为缺失的方法函数提供了签名,子类必须创建符合基类定义的接口。

抽象基类可以保证一组自定义类的一致性,可以用它创建一些通用、可重用的抽象。

抽象基类的核心定义在 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