全局单身汉:深入理解 Python 中的单例对象

文章目录

  • 参考
  • 描述
  • 单例对象
      • 单例对象
      • 单例对象的优缺点
          • 单例对象的优点
          • 单例对象的缺点
      • 单例对象的应用场景
      • 实现单例对象的多种方式
  • Python 与单例对象
      • 铺垫
          • id()
          • 关键字 is
          • 相等运算符
      • 基本数据类型与单例对象
          • 基本数据类型
          • 关联
  • 单例对象的特点
      • 进程隔离
      • 全局访问

参考

项目 描述
搜索引擎 Google 、Bing
Python 官方文档

描述

项目 描述
Python 解释器 3.10.6

单例对象

单例对象

在 Python 中,单例对象是一种设计模式,旨在确保在应用程序中只有一个特定类的实例。这意味着无论创建多少个该类的实例,都将始终引用相同的实例。

单例对象的优缺点

单例对象的优点
  1. 节约资源
    单例对象只有一个实例,可以减少内存占用和系统资源的使用,特别是在需要创建多个相同实例的场景下,使用单例对象可以显著减少系统开销,提升程序的性能和效率。

  2. 提供全局访问点
    由于单例对象只有一个实例,可以在程序中任何需要访问该对象的地方提供一个全局访问点,简化程序的逻辑和开发难度。

  3. 保证对象的唯一性
    由于单例对象只有一个实例,可以确保对象的唯一性,避免由于多个实例之间的状态变化导致程序出错的情况。

单例对象的缺点
  1. 违反了单一职责原则
    单例对象通常既充当了对象的创建者,又充当了对象的访问者,从而增加了对象的职责,使得代码难以理解和维护。

  2. 可能会导致全局变量污染
    由于单例对象提供了全局访问点,可能会导致在程序中过度使用全局变量,增加代码的耦合性和不可预测性。

单例对象的应用场景

  1. 数据库连接池
    在一个系统中,需要频繁地访问数据库,而每次访问数据库都需要进行连接和认证等操作,这样会严重影响系统性能。因此,可以使用单例模式来创建数据库连接池,使得连接对象只被创建一次,可以被多个线程重用,从而提高了系统的性能。

  2. 日志系统
    在一个系统中,需要记录各种事件和操作的日志,而这些日志可能需要在整个系统中被使用和查询。因此,可以使用单例模式来创建一个全局的日志系统,将日志写入一个文件或者数据库中,从而方便系统的管理和查询。

  3. 配置管理器
    在一个系统中,可能需要读取和管理各种配置信息,如数据库连接信息、服务器地址、认证信息等等。因此,可以使用单例模式来创建一个全局的配置管理器,方便系统的配置和管理。

  4. 线程池
    在一个系统中,可能需要创建多个线程来完成各种任务,而每次创建线程都需要一定的开销和资源。因此,可以使用单例模式来创建一个线程池,使得线程对象只被创建一次,可以被多个任务重用,从而提高了系统的性能。

  5. 状态管理器
    在一个系统中,可能需要管理各种状态信息,如用户登录状态、购物车状态、订单状态等等。因此,可以使用单例模式来创建一个全局的状态管理器,方便系统的状态管理和查询。

实现单例对象的多种方式

  1. 模块
    将实例化的对象定义为一个模块级别的变量,当第二次导入该模块时,将不会重新创建该对象,而是直接使用之前创建的对象。

  2. 装饰器
    使用一个装饰器函数将类装饰成单例类,装饰器函数通过保存一个类的实例来确保只有一个实例被创建并且提供全局访问点。

  3. 元类
    通过定义一个元类,在类被创建时控制其实例化过程,可以确保在程序中只存在一个类的实例。

Python 与单例对象

铺垫

id()

在 Python 中,id() 函数是一个内置函数,用于获取对象的身份标识。每个对象都有一个唯一的身份标识,用于标识 该对象在内存中的位置。id() 函数接受一个 对象 作为参数,并返回该对象的身份标识。

举个栗子

# 返回一个列表对象的内存地址
print(id([]))

# 返回一个数值字面量的内存地址
# Python 中,一切皆对象
print(id(369))

执行效果

2197257094528
2197313149328
关键字 is

在 Python 中,is 是一个关键字,用于比较两个对象的身份标识是否相同。is 运算符用于检查两个对象是否是同一个对象,即它们是否引用同一个内存地址中的对象。is 关键字的两个操作数若为同一个对象,则返回 True,否则返回 False

举个栗子

arr = []
arr1 = arr
num = 369

# 输出各个变量所处的内存空间地址
print(id(arr))
print(id(arr1))
print(id(num))

# 通过相等运算符判断内存空间地址是否相同
print(id(arr) == id(arr1))
print(id(arr) == id(num))

# 使用 is 关键字判断内存空间地址是否相同
print(arr is arr1)
print(arr is num)```

`执行效果`

```python
2505215018368
2505215018368
2505271997872
True
False
True
False
相等运算符

在 Python 中,相等运算符是 ==,用于比较两个对象的值是否相等。如果两个对象的值相等,则相等运算符返回 True,否则返回 False相等运算符会比较两个对象的值,而不会比较它们的身份标识(即内存地址)是否相同。因此,即使两个对象的身份标识不同,只要它们的值相等,相等运算符也会返回 True。对此,请参考如下示例:

arr = [1, 2, 3]
arr1 = [1, 2, 3]

# 分别输出两个列表所处的内存空间地址
print(id(arr))
print(id(arr1))

# 将两个列表作为 is 关键字的操作数
print(arr is arr1)

# 使用相等运算符对两个列表解析判断
print(arr == arr1)

执行效果

2477433456000
2477488503360
False
True

基本数据类型与单例对象

基本数据类型

基本数据类型指的是编程语言中最基本的数据类型,通常由语言本身提供支持,而不需要额外的库或模块。

在 Python 中,基本数据类型包括以下 四种

数据类型 描述 示例
数值类型(Number) 用于表示数值,包括整数、浮点数和复数等。 整数类型可以表示任意大小的整数,浮点数类型可以表示实数,复数类型可以表示具有实部和虚部的复数。
字符串类型(String) 由一系列字符组成,用于表示文本信息。 可以用字符串类型表示一个人的姓名、一篇文章的标题等等。
布尔类型(Boolean) 只有两个取值,TrueFalse,用于表示真值和假值。 在编写程序时,可以使用布尔类型来进行条件判断和逻辑运算。
空类型(NoneType) 表示一个空值或者空对象,通常用于表示一个不存在的值。 在 Python 中,如果没有显示指明函数的返回值,则函数默认将返回 None
关联

Python 中的基本数据类型都是不可变类型,且在程序运行期间只会创建一次,并且被存储在内存的固定位置,这种特性被称为 缓存。因此,可以认为 Python 中的基本数据类型都是单例对象,即同一个数值、字符串、布尔值或者空值在内存中只有一份。

举个栗子

a = 1
b = 1

print(id(a))
print(id(b))
print(id(1))

执行效果

1870240153840
1870240153840
1870240153840

注:

需要注意的是,这个特性仅适用于 Python 中的 基本数据类型,对于 复合数据类型(如列表、字典等)并不适用。

a = []
b = []

print(id(a))
print(id(b))
print(id([]))

执行效果

1997399098752
1997460311808
1997457222656

单例对象的特点

进程隔离

不同的进程创建的单例对象是不同的。因为在 Python 中,不同的进程会拥有自己独立的进程空间,它们之间不会共享任何资源,包括内存、文件句柄、网络连接等等。因此,在不同的进程中创建的单例对象是完全独立的,它们不会相互干扰。

如果需要在多个进程中共享单例对象,可以使用一些特殊的机制来实现,如共享内存、消息队列等等。这些机制可以在不同的进程之间传递数据,并且可以保证数据的同步和一致性。在使用这些机制时,需要注意线程安全和进程安全的问题,避免出现竞态条件、死锁等问题。

全局访问

Python 内置的基本数据类型是单例对象,因为它们经常被使用且不可变,使用单例模式可以节省内存空间,减少对象的创建和销毁,提高程序的效率。同时,这些基本数据类型被设计为可被全局访问,因为它们是开发者在 Python 代码中最常用的数据类型之一,开发者需要在程序的不同部分共享这些数据类型的值,以方便实现程序的逻辑。为此,Python 将这些数据类型的实例作为全局单例对象进行管理,以便在程序中的任何位置都可以访问到它们。

你可能感兴趣的:(Python,python,设计模式,单例对象,单例模式,基本数据类型)