91 | Python 设计模式 —— 单例模式

时间:2024-10-13 11:04:47

单例模式是设计模式中的一员,它着重解决的问题是如何确保一个类只有一个实例,并且提供全局访问点。在实际开发中,我们经常会遇到需要全局唯一对象的情况,比如全局配置管理器、日志记录器、数据库连接池等。单例模式就是为了满足这种需求而诞生的,通过巧妙地控制实例的创建过程,确保只有一个实例存在,并且可以在程序的任何地方方便地获取这个实例。

文章目录

  • 单例模式
    • 什么是单例模式?
    • 实现单例模式
      • 1. 使用模块级别变量
      • 2. 使用类属性
      • 3. 使用装饰器
    • 单例模式例子
      • 1. 简单的配置管理器
      • 2. 简单的日志记录器
      • 3. 简单的计数器

单例模式

什么是单例模式?

单例模式是一种创建型设计模式,旨在确保一个类只有一个实例,并提供全局访问点。在软件开发中,有些类只需要一个全局唯一的实例,而不需要创建多个实例。单例模式就是为了满足这种需求而设计的。

使用单例模式的好处包括:

  • 节省内存:由于单例模式只有一个实例,可以减少重复创建对象的内存开销。
  • 全局访问:通过全局访问点,可以方便地在任何地方获取该类的实例。
  • 避免竞争条件:单例模式可以防止多线程环境下的竞争条件,确保只有一个实例被创建。

实现单例模式

在 Python 中,实现单例模式有多种方法,以下是其中几种常见的方式。

1. 使用模块级别变量

Python 的模块在程序运行期间只会被加载一次,因此,可以将单例对象直接定义在模块级别变量中。

# 

class Singleton:
    def __init__(self):
        # 初始化操作
        pass

# 创建单例对象
singleton_instance = Singleton()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在其他模块中,可以直接导入 并使用 singleton_instance 对象。

# 

from singleton import singleton_instance

# 使用单例对象
singleton_instance.some_method()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2. 使用类属性

在 Python 中,类的属性是共享的,因此可以将单例对象作为类属性,并通过类方法来获取该对象。

class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            # 可以在这里进行一些初始化操作
        return cls._instance

    def some_method(self):
        # 做一些事情
        pass
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在Python中,以单下划线 _ 开头的变量通常被视为内部变量或私有变量。这是一种约定,用于指示变量或方法是类的内部实现细节,而不是供外部直接访问或使用的公共接口。

在单例模式的实现中,我们将 _instance 定义为类级别的变量,用于保存类的唯一实例。通过在变量名前面加上单下划线 _,我们暗示这是一个内部变量,不应该直接从类的外部访问或修改它,而应该通过类的特定方法来进行操作。

虽然在Python中没有真正的私有变量(所有变量都是可访问的),但约定是,如果变量以单下划线 _ 开头,则应视为类的私有变量,其他代码应尽量避免直接访问它。

实际上,如果想要模拟私有变量,并防止意外的访问,可以在变量名前面加上双下划线 __(双下划线开头的变量名将被Python解释器重写为类名 + 变量名,从而实现名称修饰)。但对于单例模式中的 _instance 变量,通常仅使用单下划线 _ 表示它是类的内部变量,而不是真正的私有变量。

使用 __new__ 方法来控制对象的创建过程,确保只有一个实例被创建。

# 

from singleton import Singleton

# 获取单例对象
singleton_instance = Singleton()

# 使用单例对象
singleton_instance.some_method()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3. 使用装饰器

装饰器是 Python 的一种强大特性,可以在函数或类上增加额外的功能。通过装饰器,我们可以实现一个简单的单例模式。

def singleton(cls):
    instances = {}

    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return get_instance

@singleton
class Singleton:
    def __init__(self):
        # 初始化操作
        pass

    def some_method(self):
        # 做一些事情
        pass
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

使用 @singleton 装饰器来标记类为单例,当创建实例时,装饰器会确保只有一个实例被创建。

# 

from singleton import Singleton

# 获取单例对象
singleton_instance = Singleton()

# 使用单例对象
singleton_instance.some_method()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

单例模式例子

当涉及到单例模式时,实现方式并不复杂。以下是几个简单的Python代码示例,增加了中文注释:

1. 简单的配置管理器

class ConfigManager:
    _instance = None

    def __new__(cls):
        # 如果还没有实例化过,则创建一个新实例
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            # 初始化配置信息字典
            cls._instance._config = {}
        return cls._instance

    def set_config(self, key, value):
        # 设置配置信息的键值对
        self._config[key] = value

    def get_config(self, key):
        # 获取配置信息的值
        return self._config.get(key)

# 使用配置管理器设置和获取配置信息
config_manager = ConfigManager()

# 设置配置信息
config_manager.set_config('debug_mode', True)
config_manager.set_config('log_level', 'INFO')

# 获取配置信息
debug_mode = config_manager.get_config('debug_mode')
log_level = config_manager.get_config('log_level')

# 输出配置信息
print(debug_mode)  # 输出:True
print(log_level)   # 输出:INFO
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

2. 简单的日志记录器

class Logger:
    _instance = None

    def __new__(cls):
        # 如果还没有实例化过,则创建一个新实例
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            # 初始化日志数据列表
            cls._instance._log_data = []
        return cls._instance

    def log(self, message):
        # 记录日志信息
        self._log_data.append(message)

    def get_logs(self):
        # 获取已记录的日志信息
        return self._log_data

# 使用日志记录器记录和获取日志信息
logger = Logger()

# 记录日志
logger.log('应用程序启动')
logger.log('正在处理数据...')
logger.log('应用程序完成')

# 获取日志信息
logs = logger.get_logs()

# 输出日志信息
for log in logs:
    print(log)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

输出:

应用程序启动
正在处理数据...
应用程序完成
  • 1
  • 2
  • 3

3. 简单的计数器

class Counter:
    _instance = None

    def __new__(cls):
        # 如果还没有实例化过,则创建一个新实例
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            # 初始化计数值为0
            cls._instance._count = 0
        return cls._instance

    def increment(self):
        # 增加计数值
        self._count += 1

    def get_count(self):
        # 获取当前计数值
        return self._count

# 使用计数器增加和获取计数值
counter = Counter()

# 增加计数值
counter.increment()
counter.increment()
counter.increment()

# 获取计数值
count = counter.get_count()
print(count)  # 输出:3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

补充: __init____new__ 都是Python中的特殊方法,用于在创建对象时进行不同的操作。

  1. __new__ 方法:
  • __new__ 是一个类级别的方法,用于创建一个新的实例对象。它在实例化对象之前调用,并返回一个新的实例。这个方法是一个静态方法,第一个参数是类本身(通常命名为 cls),其后是其余的初始化参数。

  • __new__ 的主要作用是控制对象的创建过程,可以通过返回不同的实例对象来实现特殊的对象创建方式。在一般情况下,我们不需要自己去实现 __new__ 方法,因为Python会自动帮我们创建实例并调用 __init__ 方法进行初始化。

  • 如果 __new__ 返回一个已存在的实例对象,则会直接调用该实例的 __init__ 方法,不会再创建新的实例。

  1. __init__ 方法:
  • __init__ 是一个实例级别的方法,用于对新创建的实例进行初始化操作。它在对象创建之后调用,接收创建好的实例作为第一个参数(通常命名为 self),其后是其他的初始化参数。

  • __init__ 主要用于设置对象的初始状态,对对象的属性进行赋值和其他初始化操作。

  • 通常情况下,我们会自定义 __init__ 方法来在对象创建后初始化对象的状态,但不需要显式地调用它,因为在对象创建时,Python会自动调用 __init__ 方法。

简而言之,__new__ 方法用于创建对象,控制对象的创建过程,并返回一个新的实例;而 __init__ 方法用于初始化对象,在对象创建后对其进行一些设置和赋值操作。两者在对象创建时的执行顺序是:__new__ 先于 __init__