Python Tricks - Classes & OOP(4)

Abstract Base Classes Keep Inheritance in Check

抽象基类保持检查继承???
Abstract Base Classes (ABCs) ensure that derived classes implement particular methods from the base class. In this chapter you’ll learn about the benefits of abstract base classes and how to define them with Python’s built-in abc module.
这一章节我们江雪的是抽象基础类的好处和如何使用python内置的基础模块去定义它们。

So what are Abstract Base Classes good for? A while ago I had a discussion at work about which pattern to use for implementing a maintainable class hierarchy in Python. More specifically, the goal was to define a simple class hierarchy for a service backend in the most programmer-friendly and maintainable way.
那么抽象基类有什么好处呢?刚才我有个讨论在工作中,要使用哪个模式来实现可维护的python中的类层次结构。更具体地说,目标是在大多数情况下为服务后端定义简单的类层次结构程序员友好和可维护的方式。

We had a BaseService class that defined a common interface and several
concrete implementations. The concrete implementations do different
things but all of them provide the same interface (MockService,
RealService, and so on). To make this relationship explicit, the concrete
implementations all subclass BaseService.
意思就是确切的执行类都是基于基础服务(?基础类)

To make this code as maintainable and programmer-friendly as possible
we wanted to make sure that:

  • instantiating the base class is impossible; and
  • forgetting to implement interface methods in one of the subclasses
    raises an error as early as possible.
    示例一个基础类是不可能的,忘记在其中一个子类中实现接口方法
    尽早引发错误。说实话这两点还是不太理解,所以继续往下看。

Now why would you want to use Python’s abc module to solve this problem? The above design is pretty common in more complex systems. To enforce that a derived class implements a number of methods from the base class, something like this Python idiom is typically used:

class Base:
  def foo(self):
    raise NotImplementedError()
  def bar(self):
    raise NotImplementedError()

class Concrete(Base):
  def foo(self):
    return 'foo() called'

  # Oh no, we forgot to override bar()...
  # def bar(self):
  # return "bar() called"

So, what do we get from this first attempt at solving the problem? Calling methods on an instance of Base correctly raises NotImplementedError exceptions:

>>> b = Base()
>>> b.foo()
NotImplementedError

Furthermore, instantiating and using Concrete works as expected. And, if we call an unimplemented method like bar() on it, this also raises an exception:

>>> c = Concrete()
>>> c.foo()
'foo() called'
>>> c.bar()
NotImplementedError

This first implementation is decent, but it isn’t perfect yet. The downsides
here are that we can still:
第一个实现不错,但是不是完美的。下面是他的缺陷。

- instantiate Base just fine without getting an error; and
- provide incomplete subclasses—instantiating Concrete will not raise an
 error until we call the missing method bar().

其实这意思就是我们实例化base类没有报错而且实例化concrete类的时候没有报错,直到我们调用了遗失的bar方法。

With Python’s abc module that was added in Python 2.6, we can do better and solve these remaining issues. Here’s an updated implementation using an Abstract Base Class defined with the abc module:

from abc import ABCMeta, abstractmethod

class Base(metaclass=ABCMeta):
  @abstractmethod
  def foo(self):
    pass
  @abstractmethod
  def bar(self):
    pass

class Concrete(Base):
  def foo(self):
    pass

# We forget to declare bar() again...

我们使用abc模块中的抽象基础类对上面的实现进行了升级。

This still behaves as expected and creates the correct class hierarchy:

assert issubclass(Concrete, Base)

Yet, we do get another very useful benefit here. Subclasses of Base raise a TypeError at instantiation time whenever we forget to implement any abstract methods. The raised exception tells us which method or methods we’re missing:

>>> c = Concrete()
TypeError:
"Can't instantiate abstract class Concrete
with abstract methods bar"

这个方法会告诉我们在实例化对象的时候会提醒我们类中有一些方法没有定义。

Without abc, we’d only get a NotImplementedError if a missing method was actually called. Being notified about missing methods at instantiation time is a great advantage. It makes it more difficult to write invalid subclasses. This might not be a big deal if you’re writing new code, but a few weeks or months down the line, I promise it’ll be helpful.

This pattern is not a full replacement for compile-time type checking, of course. However, I found it often makes my class hierarchies more robust and more readily maintainable. Using ABCs states the programmer’s intent clearly and thus makes the code more communicative. I’d encourage you to read the abc module documentation and to keep an eye out for situations where applying this pattern makes sense.

Key Takeaways
  • Abstract Base Classes (ABCs) ensure that derived classes implement
    particular methods from the base class at instantiation
    time.
  • Using ABCs can help avoid bugs and make class hierarchies easier
    to maintain.

你可能感兴趣的:(Python Tricks - Classes & OOP(4))