我为什么要创建一个不能被实例化的类

简介: 我为什么要创建一个不能被实例化的类

摄影:产品经理感谢小何的上等牛肉

当我们创建一个Python 类并初始化时,一般代码这样写:

class People:
    def __init__(self, name):
        self.name = name
    def say(self):
        print(f'我叫做:{self.name}')
kingname = People('kingname')
kingname.say()

运行效果如下图所示:

上面是众所周知的写法。但如果有一天,你发现我写了这样一个类:

class People:
    def say(self):
        print(f'我叫做:{self.name}')
    def __new__(self):
        raise Exception('不能实例化这个类')
kingname = People()
kingname.say()

一旦初始化就会报错,如下图所示:

你会不会感到非常奇怪?一个不能被初始化的类,有什么用?

这就要引入我们今天讨论的一种设计模式——混入(Mixins)。

Python 由于多继承的原因,可能会出现钻石继承[1]又叫菱形继承。为了保留多继承的优点,但又摒除缺点,于是有了混入这种编程模式。

Mixins 是一个 Python 类,它只有方法,没有状态,不应该被初始化。它只能作为父类被继承。每个 Mixins 类只有一个或者少数几个方法。不同的 Mixin 的方法互不重叠。

例如,我们现在有一个类 People

class People():
    def __init__(self, name, age):
        self.age = age
        self.name = name
    def say(self):
        print(f'我叫做:{self.name},我今年{self.age}岁')
kingname = People('kingname', 28)
pm = People('pm', 25)
kingname > pm

显然,这样写会报错,因为两个类的实例是不能比较大小的:

但在现实生活中,当我们说 某人比另一个人大时,实际上是指的某人的年龄比另一人年龄大。所以如果要让这两个实例比较大小,我们需要实现多个魔术方法:

class People():
    def __init__(self, name, age):
        self.age = age
        self.name = name
    def say(self):
        print(f'我叫做:{self.name},我今年{self.age}岁')
    def __ne__(self, other):
        return self.age != other.age
    def __lt__(self, other):
        return self.age < other.age
    def __le__(self, other):
        return self.age <= other.age
    def __gt__(self, other):
        return self.age > other.age
    def __ge__(self, other):
        return self.age >= other.age

运行效果如下图所示:

但如果这几个魔术方法会在多个类中使用,那么我们就可以把它抽出来,作为一个父类:

class ComparableMixin(object):
    def __ne__(self, other):
        return self.age != other.age
    def __lt__(self, other):
        return self.age < other.age
    def __le__(self, other):
        return self.age <= other.age
    def __gt__(self, other):
        return self.age > other.age
    def __ge__(self, other):
        return self.age >= other.age

然后在使用 People 类继承它:

本质上,混入的写法与普通的类继承类没有什么区别。但是 在写 Mixins 类的时候,我们不会写__init__方法,也不会写类属性。并且 Mixin 类中的方法看起来更像是工具方法。

我们可以写很多个 Mixin 类,然后用一个子类去继承他们。由于这些 Mixin 类提供的各个工具方法互不相关,所以不存在菱形继承的问题。但是在子类中却可以分别调用这些工具方法,从而扩展子类的功能。

最后,我们对比一下抽象类(Abstract Class)接口(Interface)混入(Mixins)的区别:

抽象类:

  • 包含一个或多个抽象方法。
  • 允许包含状态(实例变量)和非抽象方法。

接口:

  • 只能包含抽象方法。

混入:

  • 不能包含状态(实例变量)。
  • 包含一个或多个非抽象方法。
目录
相关文章
|
6月前
|
C++
c++类&对象
c++类&对象
50 3
|
5月前
|
JavaScript 前端开发
浅谈一下实例化
浅谈一下实例化
|
6月前
|
存储 Java 编译器
类、对象、方法
摘要: 本文介绍了面向对象编程的概念,以京东购买手机为例,展示了如何通过分类和参数选择商品,强调软件与现实生活的对应关系。柯南三步走揭示了京东如何通过搜索和筛选帮助用户找到所需商品,而这一切背后的编程思想即为面向对象编程。面向对象编程涉及抽象、自定义类型和实例化对象等步骤,其中自定义类型(如Java中的类)用于封装现实生活中的复杂数据。文章还讲解了如何定义类、实例化对象以及访问权限修饰符、构造方法、this关键字、方法的使用,强调了方法参数和返回值在不同数据类型上的处理差异。整个讨论旨在阐明Java中面向对象编程的基本原理和实践应用。
44 5
|
6月前
|
存储 C++
C++对象和类
C++对象和类
38 0
|
6月前
|
存储 C#
C#对象和类
C#对象和类
45 0
|
6月前
|
存储 算法 Java
第 4 章 对象与类(上)
第 4 章 对象与类
79 0
|
6月前
|
存储 Java 编译器
第 4 章 对象与类(下)
第 4 章 对象与类
136 0
|
存储
什么是实例化?
什么是实例化?
84 0
|
编译器 C语言 C++
C++ 类 & 对象
【摘要】 C++ 类 & 对象C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。类中的数据和方法称为类的成员。函数在一个类中被称为类的成员。C++ 类定义定义一个类,本质上是定义一个数据类型的蓝图。这实际上并没有定义任何数据,但它定义了类的名称意味着什么,也就是...