Python 싱글톤(Singleton) 패턴 적용

2021. 10. 18. 17:38[개발] 지식/Python

Python 싱글톤 패턴 적용

싱글톤 패턴 적용을 위한 아래 코드 이해를 위해서는 일단 cls와 self의 차이와 용도에 대해서 알아야 한다.

아래는 SQLAlchemy의 sessionmaker를 사용한 세션 팩토리 클래스이다.

class SessionFactory():

    def __init__(self):
        self._engine = create_engine(
            "database_url",
            echo=True,
            pool_recycle=900,
            pool_pre_ping=True,
        )
        self._session = sessionmaker(autocommit=False, autoflush=False, bind=self._engine)

    def getSession(self):
        return self._session

이를 사용하려면 아래와 같이 인스턴스를 생성해주고, getSession 함수를 통해 세션을 리턴받는다.

sessionFactory = SessionFactory()
session = sessionFactory.getSession()

그리고 이 세션을 가지고 다니면서 사용하면 된다.

하지만 이는 약간의 위험성을 갖게 된다.

SessionFactory 의 인스턴스를 생성할때마다 DB Connection이 중복으로 생성되기 때문이다.

한번 생성한 세션을 계속 사용하면 된다고 하지만, 공동작업이 이루어지는 개발의 특성상 그것이 보장이 된다고 할 수는 없다.

따라서 인스턴스를 계속해서 생성하더라도 항상 동일한 세션을 받도록 보장하기 위해서는 싱글톤 패턴을 적용해야 한다.

class SingletonInstance:
  __instance = None

  @classmethod
  def __getInstance(cls):
    return cls.__instance

  @classmethod
  def instance(cls, *args, **kargs):
    cls.__instance = cls(*args, **kargs)
    cls.instance = cls.__getInstance
    return cls.__instance

위 코드는 싱글톤 인스턴스를 리턴받기 위한 클래스이다.

이를 SessionFactory 클래스가 상속받아 사용하게 될 것이다.

@classmethod 어노테이션은 해당 함수가 static 임을 명시한다. 즉 인스턴스를 생성하지 않고 사용하 할 수 있는 함수라는 뜻이다.

따라서 SessionFactory 클래스가 이를 상속받았다면 아래와 같이 인스턴스를 생성하지 않고 곧바로 instance 함수를 호출할 것이다.

sessionFactory = SessionFactory.instance()

아래 부분을 좀 더 들여다 보자.

@classmethod
  def instance(cls, *args, **kargs):
    cls.__instance = cls(*args, **kargs)
    cls.instance = cls.__getInstance
    return cls.__instance

cls 는 인스턴스가 아닌 클래스 자체를 참조한다.

따라서 cls(*args, **kargs) 이 부분은 SessionFactory의 생성자를 호출해서 그 인스턴스를 cls.__instance 이라는 static 필드에 저장하라는 뜻과 같다.

또한 cls.instance = cls.__getInstance 이 구문을 통해 instance 함수 자체를 __getInstance 함수로 대체한다. 앞으로 SessionFactoryinstance static 함수를 호출하면 사실상 __getInstance 가 호출되는 것과 마찬가지이므로 기존에 저장된 cls.__instance 를 리턴한다.

class SessionFactory(SingletonInstance):

    def __init__(self):
        self._engine = create_engine(
            "database_url",
            echo=True,
            pool_recycle=900,
            pool_pre_ping=True,
        )
        self._session = sessionmaker(autocommit=False, autoflush=False, bind=self._engine)

    def getSession(self):
        return self._session

SessionFactory 클래스를 SingletonInstance 를 상속받도록 변경한다.

그리고 아래와 같이 사용한다.

sessionFactory = SessionFactory.instance()

그리고 아래처럼 생성한 session 객체는 동일한 인스턴스에서 가져왔으므로 동일성이 보장된다.

sessionFactory1 = SessionFactory.instance()
sessionFactory2 = SessionFactory.instance()

print(sessionFactory1.getSession())
print(sessionFactory2.getSession())

위의 코드는 예시일뿐 모범적인 코드는 아니다.

특히 웹에서는 싱글톤을 보장하는 더 좋은 기법들이나 프레임워크 기능들이 있기 때문에 해당 기능을 활용하는 것이 좋다.

다만 이러한 방식으로 싱글톤을 구현할 수 있다는 것을 아는 것에 의의가 있다고 볼 수 있겠다.

Ref.

여러가지 싱글톤(singleton) 구현방법 - Python Snippets - 파이썬 조각 코드 모음집

<