在处理编程和对象时,强调构成问题而不是身份问题,强调hasattr函数而不是isinstance函数。抽象基类是一个分配身份的机制。抽象基类也提供了一个标明抽象方法的机制,就是要求其他实现提供关键性功能,这些功能是在基类中实现中不主动提供的功能。
抽象基类提供了声明一个类是另一个类的派生类的机制(无论它是否是另一个类的派生类),该机制没有影响实际的对象继承关系或是改变方法解析顺序。
抽象基类提供了一种要求子类实现指定协议的方式。如果一个抽象基类要求实现指定方法,而子类没有实现这个方法,然后当试图创建子类时会抛出错误。
abc模块是表示抽象基类的模块,它提供一个名为ABCMeta的元类,任何抽象基类必须使用ABCMeta元类。
声明虚拟子类
>>>import abc
>>>class AbstractDict(metaclass = ABCMeta):
def foo(self):
return None
>>>AbstractDict.register(dict)
>>>{}.foo()
Traceback
...
...AttributeError:'dict' object has no attribute 'foo'
dict的方法解析并没有发生改变
>>>isinstance({}, AbstractDict)
True
>>>isinstance(AbstractDict, {})
False
register方法是ABCMeta元类的类所提供的方法,用来注册应该被注册为ABCMeta子类的新类。
>>>@AbstractDict.register
...class DictLikeAbstract(object):
... pass
>>>issubclass(DictLikeAbstract, AbstractDict)
>>>True
__subclasshook__:此方法必须定义为一个类方法,并且使用@classmethod装饰器定义,接受一个额外的位置参数是被测试的类
>>> import abc
>>> class AbstractDuck(object):
__metaclass__ = abc.ABCMeta
@classmethod
def __subclasshook__(cls, other):
quack = getattr(other, 'quack', None)
return callable(quack)
>>> class Duck(object):
def quack(self):
pass
>>> class NotDuck(object):
quack = 'foo'
>>> issubclass(Duck, AbstractDuck)
True
>>> issubclass(NotDuck, AbstractDuck)
False
>>> AbstractDuck.register(NotDuck)
>>> issubclass(NotDuck, AbstractDuck)
False
如果子类中有可调用的quack方法则是AbstractDuck的子类。__subclasshook__方法被定义时优于register方法。
>>> class Task(object):
def __init__(self):
self.runs = []
def run(self):
start = datetime.now()
result = self._run()
end = datetime.now()
self.runs.append({
'start': start,
'end': end,
'result': result,
})
return result
def _run(self):
raise NotImplementedError('Task subclasses must define a _run method.')
>>> t = Task()
>>> t.run()
Traceback (most recent call last):
File "", line 1, in
t.run()
File "", line 6, in run
result = self._run()
File "", line 15, in _run
raise NotImplementedError('Task subclasses must define a _run method.')
NotImplementedError: Task subclasses must define a _run method.
Task提供一个shell方法_run,任何未能重写_run的子类大都会引发NotImplementedError
>>> class SubTask(Task):
def _run(self):
return 2+2
>>> subtask = SubTask()
>>> subtask.run()
4
抽象基类可以被明确提出作为满足特定需求的解决方案,依据这一概念,理想情况下使用抽象基类后应该仅有一种实现这种特定需求的正确方法。
抽象属性
>>> class AbstractClass(object):
__metaclass__ = abc.ABCMeta
@abc.abstractproperty
def foo(self):
pass
>>> class InvalidChild(AbstractClass):
pass
>>> ic = InvalidChild()
Traceback (most recent call last):
File "", line 1, in
ic = InvalidChild()
TypeError: Can't instantiate abstract class InvalidChild with abstract methods foo
>>> class ValidChild(AbstractClass):
def foo(self):
return 'foo'
>>> vc = ValidChild()
>>> vc.foo()
'foo'
>>> InvalidChild.foo()
Traceback (most recent call last):
File "", line 1, in
InvalidChild.foo()
TypeError: 'abstractproperty' object is not callable
重写抽象方法的子类才能被实例化
抽象类或静态方法
class AbstractClass(metaclass=abc.ABCMeta):
@classmethod
@abc.abstractmethod
def foo(cls):
return 42
>>>class InvalidChild(AbstractClass):
pass
>>>ic = InvalidChild()
Traceback
....
TypeError:Can't instantiate abstract class....
抽象方法本身能够被直接调用,且不会引发任何错误
>>>InvalidChild.foo()
42
抽象基类最重要的作用是提供了一个正式和动态的方式来回答这个问题,‘你正在获取的对象是你认为的类型吗’,它可以克服一些仅仅检测特定属性是否存在或仅仅检测是否为特定类这两种方法的缺点。
《Python高级编程》——读书笔记