Python metaclass는 클래스 자체가 만들어지고 호출되는 방식을 바꾸는 고급 기능이다. Singleton 구현처럼 인스턴스 생성 시점의 동작을 가로채야 할 때 metaclass의 __call__ 흐름을 이해해야 한다.
이 글은 Python 2 스타일 __metaclass__ 예제를 바탕으로 SingletonMixin이 왜 일반 mixin이 아니라 metaclass 동작을 이용하는지 추적한 디버깅 메모다.
핵심 정리
Python에서 보통 객체를 만들면 클래스가 호출되고, 그 과정에서 인스턴스 생성 로직이 실행된다. 그런데 클래스도 객체이므로, 그 클래스를 만든 metaclass가 클래스 호출 동작에 개입할 수 있다. 원문 예제의 Singleton은 type을 상속한 metaclass이고, __call__을 오버라이드해 인스턴스가 이미 있으면 기존 객체를 돌려준다. SingletonMixin에 __metaclass__가 지정되어 있으면 해당 클래스를 호출할 때 일반 클래스의 생성 흐름만 보는 것이 아니라 metaclass의 __call__이 먼저 관여한다. 그래서 LiveStrategyManager처럼 좌변에 대입하지 않고 호출만 해도 인스턴스 생성 시도가 발생하며, 그 순간 Singleton의 __call__이 실행된다. 최신 Python 코드에서는 metaclass 지정 문법이 달라질 수 있으므로 원문은 Python 2 문맥의 동작 이해용으로 보는 것이 좋다.
- metaclass는 클래스를 만드는 클래스라고 볼 수 있다.
- type을 상속하면 사용자 정의 metaclass를 만들 수 있다.
- metaclass의 __call__은 클래스가 호출되어 인스턴스를 만들 때 개입한다.
- Singleton 패턴은 __call__에서 기존 인스턴스를 재사용하는 방식으로 구현할 수 있다.
- 원문 예제의 __metaclass__ 지정은 Python 2 스타일 문법이다.
- 클래스를 호출하는 표현만으로도 인스턴스 생성 흐름이 시작된다.
- metaclass는 강력하지만 일반 상속이나 decorator보다 이해 비용이 높다.
- 단순 기능 추가라면 metaclass보다 함수, decorator, composition을 먼저 검토하는 편이 좋다.
원문은 SingletonMixin이 실제로는 metaclass 흐름을 타고 있었다는 사실을 디버깅하며 정리한 글입니다. 보강문에서는 클래스 호출, metaclass __call__, Singleton 구현이 어떻게 이어지는지 먼저 설명했습니다. decorator라는 제목 요소는 넓은 Python 동작 원리 맥락에 남아 있지만, 이 글의 핵심은 metaclass를 통한 인스턴스 생성 제어입니다.
이어서 볼 글
- Python decorator class와 type 개념 정리 - class 객체와 type의 역할을 확인한 뒤 metaclass의 __call__을 이용한 생성 제어로 이어갈 수 있다.
metaclass
딱걸렸다. SingletonMixin은 metaclass였던것이다.
http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/#python-2-metaclass
import threading
class Singleton(type):
def __init__(cls, name, bases, dict):
super(Singleton, cls).__init__(name, bases, dict)
cls._instance = None
cls._lock = threading.Lock()
def __call__(cls, *args, **kwargs):
with cls._lock:
if cls._instance is None:
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instance
class SingletonMixin(object):
__metaclass__ = Singleton
이거 재밌는 개념이네.. 아래 코드를 보자.
class Meta(type):
pass
class Complex1(object):
pass
class Complex2(Meta):
pass
class Complex3(object):
__metaclass__ = Meta
print type(Complex1)
print type(Complex2)
print type(Complex3)
결과
<type 'type'>
<type 'type'>
<class '__main__.Meta'>
전부 type이나 object로 부터 상속받고 있으므로 new-style class들이고 (따라서 metaclass(아마도) 및 super()가 사용 가능)
원래는 type()을 하면은 <type 'type'>이렇게만 나오는데
__metaclass__ 선언을 한순간 type이 바뀜을 알 수 있다.
자, 내가 궁금했던데 아래 코드를 부르는 순간
def _start_strategies(self):
self.info('Initializing strategies')
for strategy in self._strategies:
import pdb; pdb.set_trace() # XXX BREAKPOINT
LiveStrategyManager().start(strategy, self,
paused=not settings.DEBUG)
왜 아래처럼 부모 클래스인 Singleton의 __call__이 불리냐 했던건데
class Singleton(type):
def __init__(cls, name, bases, dict):
super(Singleton, cls).__init__(name, bases, dict)
cls._instance = None
cls._lock = threading.Lock()
def __call__(cls, *args, **kwargs):
with cls._lock:
if cls._instance is None:
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instance
class SingletonMixin(object):
__metaclass__ = Singleton
알고보니 metaclass로 선언을 해 두어서, 인스턴스를 만드는 순간
(obj = LiveStrategyManager() 를 하지 않았지만
LiveStrategyManger()만 해도 좌변만 없다 뿐이지 instance creation 같다.)
아래처럼 metaclass의 __call__을 먼저 호출해주었던 거시다!
http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/
이런속성이 있으니까 여기(__call__)에다가 싱글톤을 구현하기 딱 좋았던 것..
궁금증 해결 끝!
'Programming' 카테고리의 다른 글
| Python unittest 사용법: TestCase와 test_ 메서드 (0) | 2026.05.16 |
|---|---|
| DQN Experience Replay 개념 정리 (0) | 2026.05.16 |
| DokuWiki 문법과 플러그인 기본 정리 (0) | 2026.05.16 |
| OCX 로딩 오류와 Windows 개발 이슈 정리 (0) | 2026.05.16 |
| 회귀 뜻과 회귀분석, 선형회귀, 로지스틱 회귀 정리 (0) | 2026.05.16 |
