「アプリ全体で同じ設定をどこからでも使いたい」「ロガーを毎回つくるのはムダでは?」——そんな場面、ありませんか?
その悩みをシンプルに解決してくれるのが、デザインパターンのひとつであるシングルトンパターンです。
シングルトンパターンは、特定のクラスのインスタンスがただ一つだけ存在することを保証し、その唯一のインスタンスへグローバルにアクセスできる仕組みを提供します。
Pythonの学習初期におさえておくと、設計の幅がぐっと広がります。
シングルトンパターンの目的は唯一のインスタンスをつくる・配るを徹底することです。インスタンスが一つしかないため、アプリ全体で共通の状態を扱ったり、重い初期化を一度で済ませたりできます。
たとえば以下のようなときに役立ちます。
「何度も同じものを作らず、一回作ってそれをどこでも使い回したい」——この発想がシングルトンの基本です。
まずは最もシンプルで分かりやすい実装から始めましょう。ポイントは「__new__で作成を制御し、__init__は一度だけ初期化する」ことです。
class Singleton:
_instance = None # クラス全体で共有する格納先
def __new__(cls, *args, **kwargs):
# まだインスタンスがなければ作る。あればそれを返す。
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
def __init__(self, value=None):
# __init__は何度も呼ばれ得るため、初期化済みかをフラグで判定
if not hasattr(self, 'initialized'):
self.value = value
self.initialized = True
# 使ってみる
singleton1 = Singleton("First Instance")
singleton2 = Singleton("Second Instance")
print(singleton1.value) # 出力: First Instance
print(singleton2.value) # 出力: First Instance(同じインスタンスを共有)
print(singleton1 is singleton2) # 出力: True
はじめに、クラス変数の_instanceがたった一つの入れ物になります。
__new__は「インスタンスを作る工場」の役割を持つ特別なメソッドで、ここで「もう作ったことがあるか?」をチェックします。なければsuper(...)で本当に作り、あれば作らずに既存のものを返します。
一方__init__は「作られた後の初期化」を担当しますが、Pythonでは同じインスタンスを返していても呼ばれる可能性があります。そこでinitializedというフラグを使い、初回だけ値をセットするようにしています。こうすることで、2回目以降に__init__が呼ばれても状態が上書きされません。
💡 豆知識:なぜ
_instance
という名前なの? Pythonでは、変数名の先頭にアンダースコアを付けることで「これは内部用です」というサインを示します。_instance
はクラスの内部でインスタンスを保持するための変数であり、外部から直接アクセスされることを想定していません。
「本当に同じインスタンスなの?」と不安になりますよね。そんなときはid関数でメモリアドレスを見てみましょう。
#上記コードの続き
a = Singleton("A")
b = Singleton("B")
print(id(a), id(b)) # 2つの値が同じなら同一インスタンス
同じ数字が表示されれば、aとbは完全に同じオブジェクトです。だからa.valueもb.valueも同じ値を示します。
シングルトンは非常に便利ですが、どんな場面でも使えばよいわけではありません。 向いているケースと避けた方がいいケースを理解することで、設計の柔軟性と安全性を両立できます。
シングルトンは、アプリ全体で共有すべき1つのリソースを扱うときに特に有効です。 複数作る意味がない、または作りすぎると不具合や非効率になるようなオブジェクトに適しています。
つまり、「一貫性を保ちたい」「初期化コストが高い」「共有が自然」といった特徴をもつものに向いています。
シングルトンはグローバルな状態を持つため、便利さと引き換えに管理の難しさが生まれます。 一度作ったインスタンスがアプリ全体に影響を及ぼすため、誤った使い方をするとバグや性能低下につながります。
なんでもシングルトンで解決するのは危険です。 グローバルな状態が増えるほど、テストが難しくなり、コードの見通しが悪くなることがあります。
このようにシングルトンパターンの向き、不向きを理解しておく必要もあります。
ここまで学習した内容をまとめます。
次のステップとして、似た目的で「生成を管理する」他のデザインパターン(ファクトリメソッドパターン、抽象ファクトリーパターン、ビルダーパターン)にも触れてみると、状況に応じた最適解が選べるようになります。どのパターンをいつ選ぶべきか、少しずつ感覚が磨かれていきますよ。