python抽象类(ABC)

抽象类学习笔记

基本概念

  • 抽象类在python中的库是abc,它包含的方法有:

  • ABC

  • ABCMeta

  • abstractmethod

  • abstractclassmethod

  • abstractstaticmethod

  • abstractproperty
    具体解释可以查看python手册(例子中的code都是基于python3.7实现,python 2.中的写法不一样)

什么是抽象类

  1. 我们先看一个例子
class Super:
    def method(self):
        print("in Super.method")
    def delegate(self):
        print("Super.delegate")
        self.action()
class Provider(Super):
    def action(self):
        print("in Provider.action")
X=Provider()
X.delegate()
Y=Super()
print("*"*50)
Y.delegate()

output:

Super.delegate
in Provider.action
**************************************************
Super.delegate
Traceback (most recent call last):
  File "C:/Users/yantaozh/AppData/Local/Programs/Python/Python37/try8.py", line 14, in 
    Y.delegate()
  File "C:/Users/yantaozh/AppData/Local/Programs/Python/Python37/try8.py", line 6, in delegate
    self.action()
AttributeError: 'Super' object has no attribute 'action'

注意上面的例子中Provider 实例调用了delegate方法,此时会有两个搜索:

  1. 首先搜索X.delegate()函数,搜索顺序是先搜索Providerl–>Super,此时在Super 类中找到此函数,实例会将正常类一样传递这个方法给self,
  2. self.action会对self以及以上的类进行搜索action方法,此时发现Provider本身提供了此方法,即调用
  3. 但是当我们生命一个Super的实例后去调用时,因为没有action方法而异常
    这里我们发现Super类不完整,没有实现action方法,需要由子类来填充。这种超类在python中有个专有方法即抽象超类。
    以上代码可以改为:
from abc import ABC,abstractmethod
class Super(ABC):
    def method(self):
        print("in Super.method")
    def delegate(self):
        print("Super.delegate")
        self.action()
    @abstractmethod
    def action(self):
        pass
class Provider(Super):
    def action(self):
        print("in Provider.action")
X=Provider()
X.delegate()
Y=Super()
print("*"*50)
Y.delegate()

output:

Super.delegate
in Provider.action
Traceback (most recent call last):
  File "C:/Users/yantaozh/AppData/Local/Programs/Python/Python37/try8.py", line 16, in 
    Y=Super()
TypeError: Can't instantiate abstract class Super with abstract methods action
>>> 

此时我们使用@abstractmethod 内置装饰器来装饰action方法。
通过使用抽象超类时,我们发现当实例化抽象超类的时候会出现异常,此时我们需要记住抽象超类的一个重要属性:
抽象超类不能实例化(因为有未实现的方法),继承了抽象超类的子类必须全部实现被抽象化(@abstractmethod)的方法后才能实例化。

抽象超类的示例

1. @abstractmethod

2. @abstractclassmethod

from abc import ABCMeta, abstractclassmethod

class Decorator(metaclass=ABCMeta):
    """ Acts as a base class for all decorators """

    def __init__(self):
        self.method = None
        print("init")

    def __call__(self, method):
        print("call")
        self.method = method
        print(self.method.__name__)
        return self.call
    @abstractclassmethod  # == @classmethod @abstractmethod
    def call(self, *args, **kwargs):
        print(2)
        return self.method(*args, **kwargs)
class MakeBold(Decorator):
    def call(self):
        print(4)
        return "" + self.method() + ""

class MakeItalic(Decorator):
    def call(self):
        return "" + self.method() + ""
print("*"*50)

@MakeBold()
@MakeItalic()
def say():
   return "Hello"
print(say())

output:

**************************************************
init
init
call
say
call
call
4
Hello

这里的@abstractclassmethod 来装饰一个函数call,通过子类中实现call函数来实现装饰器。
在子类中的call(self)的第一个参数应为cls,但是在这个例子中method 是属于实例的属性,所以在这里通过self来传递为call的第一个参数。在继承的类中重新定义了此函数,需要通过self
这里的装饰器可以==say=MakeBold()(MakeItalic()(say)),say()

3. @abstractstaticmethod

4. @abstractproperty

  1. read-only
    @abstractproperty 修饰了一个read-only 的方法
from abc import ABC ,abstractmethod ,abstractclassmethod,abstractstaticmethod,abstractproperty

class Base(ABC):
    def __init__(self):
        self.value = 'Spam'
    #@property
    #@abstractmethod
    @abstractproperty
    def x(self):
        pass
class Sub1(Base):
    def __init__(self):
        self._x='ham'
    @property
    def x(self):
        return self._x
ins=Sub1()
print(ins.x)

2.-read-write

from abc import ABC ,abstractmethod ,abstractclassmethod,abstractstaticmethod,abstractproperty

class Base(ABC):
    def __init__(self):
        self.value = 'Spam'
    #@property
    #@abstractmethod
    @abstractproperty
    def x(self):
        pass
    @x.setter
    #@abstractmethod
    def x(self,value):
        pass
class Sub1(Base):
    def __init__(self):
        self._x='ham'
    @property
    def x(self):
        return self._x
    @x.setter
    def x(self,value):
        self._x=value
ins=Sub1()
print(ins.x)
ins.x=6
print(ins.x)

3.单独修饰set 时

from abc import ABC ,abstractmethod ,abstractclassmethod,abstractstaticmethod,abstractproperty

class Base(ABC):
    def __init__(self):
        self._x = 'Spam'
    @property
    #@abstractmethod
    #@abstractproperty
    def x(self):
        return self._x
        
    @x.setter
    @abstractmethod
    def x(self,value):
        pass
class Sub1(Base):
    @Base.x.setter
    def x(self,value):
        self._x=value
ins=Sub1()
print(ins.x)
ins.x=6
print(ins.x)

5. 特殊的register使用

from abc import ABC ,abstractmethod ,abstractclassmethod,abstractstaticmethod,abstractproperty

class Base(ABC):
    @abstractmethod
    def method_1(self):
        print("Base method_1")
    def method_2(self):
        print("Base method_2")
class Sub1:
    def method_2(self):
        print("Sub1 method_2")
class Sub2(Base):
    def method_1(self,a='None'):
        print("Sub2 method_1")
    pass
Base.register(Sub1)
print(issubclass(Sub1,Base))
i_sub1=Sub1()
i_sub1.method_2()
i_sub2=Sub2()
i_sub2.method_1()

output:

True
Sub1 method_2
Sub2 method_1

这里的register将Sub2变成了Base的一个虚拟子类,此时在Sub2中不需要定义抽象方法也能实例化。

这章中的所有的抽象方法的重写,不仅可以重写内容,参数也可以改变

你可能感兴趣的:(python抽象类(ABC))