07 抽象基类
7.1 使用抽象基类
- 抽象基类的基本目的就是提供有点形式化的方法,来测试一个对象是否符合特定规范。
isinstance({}, dict)
hasattr({}, "__getitem__")
7.2 声明虚拟子类
-
abc
模块提供的第一个内容是名为ABCMeta
的元类 。所有抽象基类可以任意地声明它是任意具体类的父类(不是派生类),包括在标准库的具体类。ABCMeta
的实例通过使用register
方法提供了对声明的实现(这些使用ABCMeta
作为他们元类的类都是类本身) -
这并没有对
dict
类本身进行任何修改,这样做就是的dict
对象也被标识为AbstractDict
的实例,并且现在的dict
自身也被标识为一个AbstractDict
的子类import abc class AbstractDitct(metaclass=abc.ABCMeta): def foo(self): return "123" print(AbstractDitct.register(dict)) # <class "dict"> print(isinstance({}, AbstractDict)) # True print(issubclass(dict, AbstractDict)) # True
import abc class MySequence(metaclass=abc.ABCMeta): pass print(MySequence.register(list)) print(MySequence.register(tuple)) print(type(MySequence)) # <class 'abc.ABCMeta'> class CustomListClass(): pass print(MySequence.register(CustomListClass)) print(issubclass(CustomListClass, MySequence))
-
使用
register
作为装饰器import abc class MySequence(metaclass=abc.ABCMeta): pass print(MySequence.register(list)) print(MySequence.register(tuple)) print(type(MySequence)) # <class 'abc.ABCMeta'> @MySequence.register class CustomListClass(): pass print(issubclass(CustomListClass, MySequence))
-
鸭子类型 范例最基本的问题是,一个对象是否有某个属性或方法(比如叫起来像只鸭子),而不是对象是否是这个或那个类的子类
import abc class AbstractDuck(metaclass=abc.ABCMeta): @classmethod def __subclasshook__(cls, other): # 优先register quack = getattr(other, "quack", None) # callable() 是否可调用 return callable(quack) class Duck(): def quack(self): pass class NotDuck(): quack = "foo" print(issubclass(Duck, AbstractDuck)) print(issubclass(NotDuck, AbstractDuck))
import abc class AbstractDuck(metaclass=abc.ABCMeta): @classmethod def __subclasshook__(cls, other): quack = getattr(other, "quack", None) # callable() 是否可调用 # return callable(quack) if callable(quack): return True return NotImplementedError class Duck(): def quack(self): pass class NotDuck(): quack = "foo" print(issubclass(Duck, AbstractDuck)) print(AbstractDuck.register(NotDuck)) print(issubclass(NotDuck, AbstractDuck)) # AssertionError
7.3 声明协议
-
使用
NotImplementedError
- 基类
Task
不能提供任务主体,这需要子类完成;未能重写_run
的子类都会引发NotImplementedError
from datetime import datetime class Task: 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 }) def _run(self): raise NotImplementedError("Task subclasses must define a _run method.") t = Task() t.run()
- 基类
-
使用元类
Task
类本身可以被实例化,但是不在声明_run
方法,因此使用run
方法会抛出AttributeError
Task
子类被创建时 元类会运行__new__
方法,解析器将不在允许创建没有_run
的子类
from datetime import datetime, timezone class TaskMeta(type): def __new__(cls, name, bases, attrs): if attrs.pop("abstract", False): print(123) return super(TaskMeta, cls).__new__(cls, name, bases, attrs) new_class = super(TaskMeta, cls).__new__(cls, name, bases, attrs) if not hasattr(new_class, "_run") or not callable(new_class._run): raise TypeError("Task subclasses must define a _run method.") return new_class class Task(metaclass=TaskMeta): abstract = True def __init__(self): self.runs = [] def run(self): start = datetime.now(tz=timezone.utc) result = self._run() end = datetime.now(tz=timezone.utc) self.runs.append({ "start": start, "end": end, "result": result }) return result class TaskSubclass(Task): # TypeError: Task subclasses must define a _run method. pass t = Task()
Task
类本身不能实例化;允许创建子类,但不允许实例化@abc.abstractmethod
指定了一个必须被所有子类重写的特定方法
import abc from datetime import datetime, timezone class Task(metaclass=abc.ABCMeta): def __init__(self): self.runs = [] def run(self): start = datetime.now(tz=timezone.utc) result = self._run() end = datetime.now(tz=timezone.utc) self.runs.append({ "start": start, "end": end, "result": result }) return result @abc.abstractmethod # def _run(self): pass class Subtask(Task): # 子类如果包含 _run 方法则可以实例化 def _run(self): return 2 st = Subtask() st.run() print(st.runs) # t = Task()
- 抽象属性:
@property
import abc class AbstractClass(metaclass=abc.ABCMeta): @property @abc.abstractmethod def foo(self): pass class VaildChild(AbstractClass): @property # 会把foo 当做属性而不是方法 def foo(self): return "bar" vc = VaildChild() print(vc.foo)
- 抽象类或静态方法
import abc class AbstractClass(metaclass=abc.ABCMeta): @classmethod @abc.abstractmethod def foo(cls): return 42 class ValidChild(AbstractClass): @classmethod def foo(cls): return "bar" print(ValidChild.foo()) vc = ValidChild() print(vc.foo())
7.4 内置抽象基类
-
最常用的抽象基类适用于集合的抽象基类,
collections.abc
-
抽象基类能被划分为两种基本类别
- 一种需要和检查单一方法(比如 IterableCallable)
- 一种作为普通内置
python
类型的替身
-
抽象基类
Callable(__call__)
Container(__contains__)
Hashable(__hash__)
Iterable(__iter__)
Sized(__len__)
Iterator(__iter__)
继承Iterable
,提供__iter__
的实现(返回自身并可以被重写),并添加抽象方法__next__
任何包含相应方法的类都会自动地被当作相关抽象基类的子类
from collections.abc import Sized class Foo: def __len__(self): return 42 print(issubclass(Foo, Sized)) # 类可以直接作为抽象基类的子类 class Bar(Sized): pass b = Bar()