抽象基类写法高版本向后兼容
python3.3之前(不包括3.3)
class MyABC(abc.ABC):
@classmethod
@abc.abstractmethod
def an_abstract_classmethod(cls, ...):
pass
@abc.abstractmethod和def之间不能有其他装饰器。
from abc import ABC, abstractmethod
class Foo(ABC):
@abstractmethod
def fun(self):
"""please Implement in subclass"""
print('please Implement in subclass')
class SubFoo(Foo):
def fun(self):
print('fun in SubFoo')
a = SubFoo()
a.fun()
from abc import ABCMeta, abstractmethod
class Bar(metaclass=ABCMeta):
@abstractmethod
def fun(self):
"""please Implement in subclass"""
print('please Implement in subclass')
class SubBar(Bar):
def fun(self):
print('fun in SubFoo')
b = SubBar()
b.fun()
from abc import ABCMeta, abstractmethod
class Base():
__metaclass__ = ABCMeta
@abstractmethod
def fun(self):
"""please Implement in subclass"""
print('please Implement in subclass')
class SubBase(Bar):
def fun(self):
print('fun in SubBase')
c = SubBase()
c.fun()
虚拟子类:指的是不通过继承而利用注册把一个类变成抽象基类的子类。
注册虚拟之类的方式是调用register方法,语法是@抽象基类名称.register。
注册虚拟子类的方式是在抽象基类上调用register方法, 经注册后的虚拟子类可以被issubclass和isinstance等函数识别,但是注册的类不会从抽象基类中继承任何方法或属性。具体可通过类属性__mro__查看类的真实继承关系
白鹅类型的一个基本特性(也是值得用水禽来命名的原因):即便不继承,也有办法把一个类注册为抽象基类的虚拟子类.这样做时我们保证注册的类忠实地实现了抽象基类定义的接口,而Python会相信我们,从而不做检查.如果我们说谎了,那么常规的运行时异常会把我们捕获.
import abc
class Tombola(abc.ABC):
@abc.abstractmethod
def load(self, iterable):
"""从可迭代对象中添加元素。"""
pass
@abc.abstractmethod
def pick(self):
"""随机删除元素,然后将其返回。
如果实例为空,这个方法应该抛出`LookupError`。
"""
pass
def loaded(self):
"""如果至少有一个元素,返回`True`,否则返回`False`。"""
return bool(self.inspect())
def inspect(self):
"""返回一个有序元组,由当前元素构成。"""
items = []
while True:
try:
items.append(self.pick())
except LookupError:
break
self.load(items)
return tuple(sorted(items))
register方法通常作为普通的函数调用,不过也可以作为装饰器使用.我们使用装饰器句法实现了TomboList类,这是Tombola 的一个虚拟子类.
from random import randrange
@Tombola.register
class TomboList(list):
def pick(self):
if self:
position = randrange(len(self))
return self.pop(position)
else:
raise LookupError('pop from empty TomboList')
load = list.extend
def loaded(self):
return bool(self)
def inspect(self):
return tuple(sorted(self))
t = TomboList([12,23,34])
isinstance(t,Tombola) # True
Tombola.register(TomboList)
只有子类实现了run方法才能运行run,否则会生成NotImplementedError错误。其中此时的Task是可以被实例化。
缺点:如果子类没有实例化run方法, 子类也可以实例化,仅当子类调用了run方法,才会报NotImplementedError错误
class Task():
def __init__(self, x, y):
self.x = x
self.y = y
def run(self):
raise NotImplemented('please Implement in subclass')
class SubTask(Task):
def __init__(self, x, y):
super().__init__(x, y)
def run(self):
print('run in SubTask')
d = SubTask(1, 3)
d.run()
自定义元类一般很少用到,基本abc模块的元类可以满足我们的需求
class TaskMeta(type):
def __new__(cls, name, bases, attrs):
new_class = super(TaskMeta, cls).__new__(cls, name, bases, attrs)
if attrs.pop('abstract', False):
return new_class
if not hasattr(new_class, 'run') or not callable(new_class.run):
raise TypeError('Please define "a run method"')
return new_class
class Task(metaclass=TaskMeta):
abstract = True
def __init__(self, x, y):
self.x = x
self.y = y
class SubTask(Task):
def __init__(self, x, y):
super().__init__(x, y)
def run(self):
print('Task(x=%s, y=%s)' % (self.x, self.y))
令抽象基类识别没有进行子类化和注册的类
参考: