Menu
CoddyTech

单例模式

本课属于 Coddy Python 学习旅程的“面向对象编程”单元。第 45 课,共 64 课。

单例模式确保一个类只有一个实例,并提供全局访问该实例的入口。这适用于数据库连接或配置设置等资源。

这是一个基本的单例实现:

class Singleton:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

__new__ 方法控制对象的创建。在创建新实例之前,它会检查实例是否已存在。

创建 Singleton 类的两个实例:

singleton1 = Singleton()
singleton2 = Singleton()

检查两个变量是否引用同一个对象:

print(singleton1 is singleton2)  # True
print(id(singleton1))            # 相同的内存地址
print(id(singleton2))            # 相同的内存地址

下面是一个更实用的例子,演示如何连接数据库:

class DatabaseConnection:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.connection = "Connected to MySQL database"
            print("Creating new database connection")
        return cls._instance
    
    def query(self, sql):
        return f"Executing: {sql}"

# 第一次访问时创建连接
db1 = DatabaseConnection()
print(db1.connection)

# 第二次访问时复用同一个连接
db2 = DatabaseConnection()
print(db2.connection)

print(db1.query("SELECT * FROM users"))
print(db1 is db2)

使用 Singleton 创建一个配置管理器:

class Config:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.settings = {}
        return cls._instance
    
    def set_setting(self, key, value):
        self.settings[key] = value
    
    def get_setting(self, key):
        return self.settings.get(key)

config1 = Config()
config1.set_setting("debug", True)

config2 = Config()
print(config2.get_setting("debug"))  # True,设置相同

输出:

True
140234567890123
140234567890123
Creating new database connection
Connected to MySQL database
Connected to MySQL database
Executing: SELECT * FROM users
True
True

关键点:Singleton 模式使用 __new__ 控制对象的创建,确保只存在一个实例。对于整个应用程序中应该只有一个副本的资源,可以使用它,例如数据库连接、日志记录器或配置管理器。请记住,所有指向 Singleton 的变量都引用内存中的同一个对象。

challenge icon

挑战

简单

在这个挑战中,你将为数据库连接类实现单例设计模式。

  • 按照 TODO 注释的要求,编辑 databaseconnection.py,实现单例模式
  • driver.py 文件包含大量测试场景,不应修改

亲手试试

from databaseconnection import DatabaseConnection

# 综合测试用例处理程序
test_case = input()

if test_case == "identity_check":
    db1 = DatabaseConnection()
    db2 = DatabaseConnection()
    print(db1 is db2)  # 应该输出 True

elif test_case == "connect_state":
    db = DatabaseConnection()
    print(f"Initial connected state: {db.connected}")  # 应该输出 False
    db.connect()
    print(f"After connect: {db.connected}")  # 应该输出 True

elif test_case == "disconnect_state":
    db = DatabaseConnection()
    db.connect()
    db.disconnect()
    print(f"After disconnect: {db.connected}")  # 应该输出 False

elif test_case == "multiple_instances_same_state":
    db1 = DatabaseConnection()
    db1.connect()
    db2 = DatabaseConnection()
    print(f"db2 connected state: {db2.connected}")  # 应该输出 True

elif test_case == "host_value":
    db = DatabaseConnection()
    print(f"Default host: {db.host}")  # 应该输出 "localhost"
    db.host = "new-server"
    db2 = DatabaseConnection()
    print(f"New instance host: {db2.host}")  # 应该输出 "new-server"

elif test_case == "init_once":
    db1 = DatabaseConnection()
    db1.host = "custom-host"
    db2 = DatabaseConnection()
    print(f"Both instances have same host: {db1.host == db2.host}")  # 应该输出 True
    print(f"Host value: {db1.host}")  # 应该输出 "custom-host"

elif test_case == "connect_message":
    db = DatabaseConnection()
    db.connect()  # 应该输出 "Connected to database at localhost"

elif test_case == "disconnect_message":
    db = DatabaseConnection()
    db.connect()
    db.disconnect()  # 应该输出 "Disconnected from database"

elif test_case == "attribute_modification":
    db1 = DatabaseConnection()
    db1.port = 3306
    db2 = DatabaseConnection()
    print(f"db2 has port attribute: {hasattr(db2, 'port')}")  # 应该输出 True
    print(f"db2 port value: {db2.port}")  # 应该输出 3306

elif test_case == "reset_connection":
    db1 = DatabaseConnection()
    db1.connect()
    print(f"Connected state: {db1.connected}")  # 应该输出 True
    db2 = DatabaseConnection()
    print(f"New instance connected state: {db2.connected}")  # 应该输出 True
    db2.disconnect()
    db3 = DatabaseConnection()
    print(f"After disconnect, new instance state: {db3.connected}")  # 应该输出 False

elif test_case == "multiple_connects":
    db = DatabaseConnection()
    db.connect()
    db.connect()
    db.connect()
    print(f"Connected state after multiple connects: {db.connected}")  # 应该输出 True
    db.disconnect()
    print(f"Connected state after disconnect: {db.connected}")  # 应该输出 False

elif test_case == "host_change_affects_message":
    db = DatabaseConnection()
    db.host = "custom-server"
    db.connect()  # 仍会输出 "Connected to database at localhost"
    # connect 方法中的消息是写死的,没有使用 host 属性
    print("Message still shows localhost: True")
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Python 在线编译器