Singleton パターン
CoddyのPythonジャーニー「Object Oriented Programming」セクションの一部 — レッスン 45/64。
シングルトンパターンは、クラスのインスタンスが1つだけであることを保証し、それへのグローバルなアクセスポイントを提供します。これは、データベース接続や設定情報のようなリソースに役立ちます。
基本的なシングルトンの実装は以下の通りです:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance__new__ メソッドはオブジェクトの生成を制御します。新しいインスタンスを作成する前に、インスタンスが既に存在するかどうかを確認します。
Singletonクラスのインスタンスを2つ作成します:
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}"
# First access creates the connection
db1 = DatabaseConnection()
print(db1.connection)
# Second access reuses the same 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重要なポイント: シングルトンパターンは __new__ を使用してオブジェクトの作成を制御し、インスタンスが1つだけ存在することを保証します。データベース接続、ロガー、設定マネージャーなど、アプリケーション全体で1つのコピーのみが必要なリソースに使用してください。シングルトンを指すすべての変数は、メモリ内の同じオブジェクトを参照することに注意してください。
チャレンジ
簡単このチャレンジでは、データベース接続クラスのためのシングルトン・デザインパターンを実装します。
- TODOコメントに従って、シングルトンパターンを実装するために
databaseconnection.pyを編集してください。 driver.pyファイルには広範なテストシナリオが含まれており、変更してはいけません。
チートシート
Singleton(シングルトン)パターンは、クラスのインスタンスが1つだけであることを保証し、それに対するグローバルなアクセスポイントを提供します。
__new__ を使用した基本的な Singleton の実装:
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 - 同じオブジェクトデータベース接続を使用した実用的な例:
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connection = "Connected to MySQL database"
return cls._instance
def query(self, sql):
return f"Executing: {sql}"設定マネージャーの例:
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)データベース接続、ロガー、設定マネージャーなど、アプリケーション全体でコピーを1つだけ保持する必要があるリソースには、Singleton を使用してください。
自分で試してみよう
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")このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。