8.12 定义接�或者抽象基类¶

问题¶

你想定义一个接�或抽象类,并且通过执行类型检查�确��类实现了�些特定的方法

解决方案¶

使用 abc 模��以很轻�的定义抽象基类:

from abc import ABCMeta, abstractmethod

class IStream(metaclass=ABCMeta):
    @abstractmethod
    def read(self, maxbytes=-1):
        pass

    @abstractmethod
    def write(self, data):
        pass

抽象类的一个特点是它�能直接被实例化,比如你想�下�这样�是�行的:

a = IStream() # TypeError: Can't instantiate abstract class
                # IStream with abstract methods read, write

抽象类的目的就是让别的类继承它并实现特定的抽象方法:

class SocketStream(IStream):
    def read(self, maxbytes=-1):
        pass

    def write(self, data):
        pass

抽象基类的一个主�用途是在代�中检查�些类是�为特定类型,实现了特定接�:

def serialize(obj, stream):
    if not isinstance(stream, IStream):
        raise TypeError('Expected an IStream')
    pass

除了继承这�方�外,还�以通过注册方��让�个类实现抽象基类:

import io

# Register the built-in I/O classes as supporting our interface
IStream.register(io.IOBase)

# Open a normal file and type check
f = open('foo.txt')
isinstance(f, IStream) # Returns True

@abstractmethod 还能注解��方法�类方法和 properties 。 你�需��这个注解紧�在函数定义���:

class A(metaclass=ABCMeta):
    @property
    @abstractmethod
    def name(self):
        pass

    @name.setter
    @abstractmethod
    def name(self, value):
        pass

    @classmethod
    @abstractmethod
    def method1(cls):
        pass

    @staticmethod
    @abstractmethod
    def method2():
        pass

讨论¶

标准库中有很多用到抽象基类的地方。collections 模�定义了很多跟容器和迭代器(�列�映射�集�等)有关的抽象基类。 numbers 库定义了跟数字对象(整数�浮点数�有�数等)有关的基类。io 库定义了很多跟I/O�作相关的基类。

你�以使用预定义的抽象类�执行更通用的类型检查,例如:

import collections

# Check if x is a sequence
if isinstance(x, collections.Sequence):
...

# Check if x is iterable
if isinstance(x, collections.Iterable):
...

# Check if x has a size
if isinstance(x, collections.Sized):
...

# Check if x is a mapping
if isinstance(x, collections.Mapping):

尽管ABCs�以让我们很方便的�类型检查,但是我们在代�中最好��过多的使用它。 因为Python的本质是一门动�编程语言,其目的就是给你更多�活性, 强制类型检查或让你代��得更��,这样�无异于�本求末。