Python Tricks-- Abstract Base Classes Keep Inheritance in Check

简介: Python Tricks-- Abstract Base Classes Keep Inheritance in Check

Python Tricks-- 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.

So what are Abstract Base Classes good for? A while agao I had a discussion at work about which pattern to use for implementing a maintainable class hierarchy in Python. More specially, the goal was to define a simple class hierarchy for a service backend in the most programmer-friendly and maintainable way.

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:

[1]: class Base:
   ...:     def foo(self):
   ...:         raise NotImplementedError()
   ...:     def bar(self):
   ...:         raise NotImplementedError()

In [2]: 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:

In [4]: b = Base()

In [5]: b.foo()
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)

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

In [6]: c = Concrete()

In [7]: c.foo()
Out[7]: 'foo() called'

In [8]: c.bar()
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)

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().

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...

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

In [9]: assert issubclass(Concrete, Base)

Yet, we do get another very useful benefit here. Subclasses of Base raise a TypeError at instantiation time

TyperError:
"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.

相关文章
|
Docker 容器
求助: 运行模型时报错module 'megatron_util.mpu' has no attribute 'get_model_parallel_rank'
运行ZhipuAI/Multilingual-GLM-Summarization-zh的官方代码范例时,报错AttributeError: MGLMTextSummarizationPipeline: module 'megatron_util.mpu' has no attribute 'get_model_parallel_rank' 环境是基于ModelScope官方docker镜像,尝试了各个版本结果都是一样的。
711 5
|
Ubuntu 计算机视觉 C++
Ubuntu系统下编译OpenCV4.8源码
通过上述步骤,你可以在Ubuntu系统上成功编译并安装OpenCV 4.8。这种方法不仅使你能够定制OpenCV的功能,还可以优化性能以满足特定需求。确保按照每一步进行操作,以避免常见的编译问题。
406 43
|
12月前
|
机器学习/深度学习 IDE 开发工具
基于OpenCV的车牌识别系统源码分享
基于OpenCV的车牌识别系统主要利用图像边缘和车牌颜色定位车牌,再利用OpenCV的SVM识别具体字符,从而达到车牌识别的效果。
536 4
基于OpenCV的车牌识别系统源码分享
|
10月前
|
人工智能 Java API
DeepSeek R1 集成难题完美解决:DeepSeek4j来帮你解决
DeepSeek R1 是一款强大的 AI 模型,但在 Java 生态中集成存在诸多挑战,如思维链丢失、参数限制和流式处理不完善等问题。DeepSeek4j 的出现解决了这些难题,它专为 Java 开发者设计,支持完整思维链保留、流畅的流式响应和简单优雅的 API。通过与 Spring Boot 的无缝集成,开发者只需几行代码即可快速接入 DeepSeek R1。此外,DeepSeek4j 提供调试页面、性能优化功能(如 GPU 加速和模型缓存),助力开发者高效利用 AI 技术,推动智能化应用落地。
|
12月前
|
开发工具 git
git fetch和 pull的区别
通过这些内容和示例,您可以系统地理解 `git fetch`和 `git pull`的区别,并在实际工作中灵活应用这两个命令,提高版本控制的效率。希望这些内容对您的学习和工作有所帮助。
620 24
|
JavaScript
使用Vue+Textarea实现在文本内容变化时自适应高度
这篇文章提供了Vue 2和Vue 3中实现Textarea在文本内容变化时自适应高度的示例代码和方法。
1152 0
使用Vue+Textarea实现在文本内容变化时自适应高度
QML 界面切换的方法
QML 界面切换的方法
976 1
|
存储 移动开发 程序员
Docker部署Dillinger个人文本编辑器
【7月更文挑战第6天】Docker部署Dillinger个人文本编辑器
589 2
|
测试技术 持续交付 数据处理
Python动态类型深度解析与实践
Python动态类型深度解析与实践
698 1
|
前端开发 NoSQL Java
毕业设计|springboot+h5的购物商城系统(一)
毕业设计|springboot+h5的购物商城系统
411 2