
Python weakref.finalize 사용법
weakref.finalize()는 객체가 가비지 컬렉션될 때 실행할 정리 콜백을 등록하는 Python 표준 라이브러리 도구다. 2026년 7월 8일 확인 기준 Python 3.14.6 공식 문서는 이 finalizer가 일반 weak reference와 달리 참조 대상 객체가 수거될 때까지 살아남도록 설계됐다고 설명한다.
실무에서는 임시 디렉터리 삭제, 외부 리소스 정리, 제3자 클래스에 대한 종료 훅 등록처럼 "객체 소멸 시점에 한 번만 cleanup을 걸고 싶다"는 상황에서 자주 검토하게 된다. 이 글은 weakref.finalize()의 동작 방식, __del__()와의 차이, alive, detach(), atexit 기준만 정리한다.
weakref.finalize()는 언제 쓰면 될까?
공식 문서 기준 weakref.finalize(obj, func, *args, **kwargs)는 obj가 가비지 컬렉션될 때 호출할 finalizer 객체를 반환한다. 핵심은 반환된 finalizer 객체를 따로 보존하지 않아도 등록이 유지된다는 점이다.
그래서 소유 객체의 생명주기에 맞춰 cleanup을 연결하되, 클래스 정의를 직접 바꾸기 어렵거나 __del__()를 넣고 싶지 않은 경우에 적합하다. 반대로 파일 핸들처럼 종료 시점이 명확하고 즉시 정리해야 하는 자원이라면 with 문이나 명시적 close()가 여전히 우선이다.
가장 먼저 알아둘 동작은 무엇일까?
| 항목 | 기준 | 실무 해석 |
|---|---|---|
| 호출 횟수 | live 상태에서 최대 1회만 콜백 실행 | 중복 cleanup 방지에 유리함 |
alive |
아직 실행되지 않았으면 true |
현재 finalizer 상태 점검용 |
detach() |
finalizer를 죽이고 등록 정보 반환 | 자동 정리를 해제하고 수동 처리로 넘길 때 사용 |
atexit |
기본값은 true |
프로그램 종료 시 살아 있는 finalizer도 호출됨 |
| 예외 전파 | GC 중 콜백 예외는 stderr에 표시되지만 호출자에게 전파되지 않음 | cleanup 실패를 비즈니스 로직 예외 처리로 기대하면 안 됨 |
반환값을 꼭 변수에 저장해야 할까?
꼭 그렇지는 않다. Python 공식 문서는 weakref.finalize()의 주요 장점으로 "반환된 finalizer 객체를 보존하지 않아도 콜백 등록이 단순해진다"는 점을 든다. 즉, 아래처럼 등록만 하고 참조를 따로 들고 있지 않아도 된다.
import shutil
import tempfile
import weakref
class TempDir:
def __init__(self):
self.name = tempfile.mkdtemp()
weakref.finalize(self, shutil.rmtree, self.name)
다만 실행 상태 확인, 조기 실행, 등록 해제가 필요하면 반환값을 저장하는 편이 낫다. 예를 들어 alive를 확인하거나 detach()를 호출하려면 finalizer 객체를 참조하고 있어야 한다.
__del__() 대신 쓰는 이유는 무엇일까?
Python 3.14.6 공식 문서는 __del__() 처리 방식이 인터프리터의 가비지 컬렉터 내부 구현에 의존해 구현체별 특성이 강하다고 설명한다. 반면 weakref.finalize()는 cleanup에 필요한 함수와 인자만 명시적으로 등록하므로, 객체 전체 상태에 의존하는 __del__()보다 더 견고한 대안이 될 수 있다.
공식 문서의 임시 디렉터리 예시도 같은 흐름을 보여 준다. self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)처럼 필요한 정리 함수와 경로만 넘기면, 객체가 수거될 때나 프로그램 종료 시점에 cleanup을 걸 수 있다.
어떤 참조를 넘기면 안 될까?
이 부분은 실수하기 쉽다. 공식 문서는 func, args, kwargs가 obj를 직접 또는 간접으로 참조하면 obj가 가비지 컬렉션되지 않을 수 있다고 경고한다. 특히 func를 obj의 bound method로 두지 말라고 명시한다.
import weakref
class Worker:
def close(self):
print("cleanup")
def arm(self):
weakref.finalize(self, self.close) # 피해야 하는 형태
위 코드는 self.close가 이미 self를 붙잡고 있으므로 finalizer의 목적과 충돌할 수 있다. 안전한 기본값은 인스턴스 메서드 대신 모듈 함수나 정적 함수에 필요한 최소 데이터만 넘기는 방식이다.
alive, 직접 호출, detach()는 어떻게 나눌까?
공식 문서에 따르면 live finalizer를 직접 호출하면 즉시 dead 상태가 되면서 func(*args, **kwargs) 결과를 반환한다. 한 번 dead가 된 뒤에는 다시 호출해도 None을 반환하고 콜백은 재실행되지 않는다.
alive는 아직 등록이 살아 있는지 확인하는 가장 단순한 상태 플래그다. detach()는 자동 정리를 해제하고 (obj, func, args, kwargs)를 돌려주므로, 소유권을 수동 정리 흐름으로 넘기고 싶을 때 맞다.
import weakref
class Resource:
pass
def cleanup(name):
print(f"{name} cleaned")
obj = Resource()
f = weakref.finalize(obj, cleanup, "cache")
if f.alive:
f() # 즉시 정리, 이후 dead
프로그램 종료 시점에는 어떻게 동작할까?
공식 문서 기준 finalizer의 atexit 속성 기본값은 true다. 따라서 프로그램이 종료될 때까지 finalizer가 살아 있다면, atexit를 false로 바꾸지 않은 한 생성의 역순으로 호출된다.
다만 문서는 인터프리터 종료 후반부처럼 모듈 전역이 None으로 바뀔 수 있는 시점에는 finalizer가 콜백을 호출하지 않는다고도 설명한다. 또 daemonic thread에서 종료 직전 finalizer를 만드는 경우에는 종료 시 cleanup이 보장되지 않을 수 있다.
실무에서 무난한 선택 기준
| 상황 | 무난한 선택 | 이유 |
|---|---|---|
| 객체 수거 시점에 보조 cleanup을 연결하고 싶다 | weakref.finalize() |
객체 수명에 맞춘 후처리를 한 번만 걸 수 있음 |
| 리소스를 반드시 즉시 닫아야 한다 | with 또는 명시적 종료 |
GC 타이밍에 의존하지 않는 편이 안전함 |
| 클래스 정의를 수정하기 어렵다 | weakref.finalize() |
제3자 클래스에도 외부에서 정리 훅을 연결할 수 있음 |
| cleanup이 객체 메서드 상태를 강하게 참조한다 | 설계 재검토 | 참조 순환으로 객체 수거가 막힐 수 있음 |
| 정리 실패를 호출자 예외로 다뤄야 한다 | 별도 명시적 종료 API | GC 중 예외는 전파되지 않음 |
FAQ
Q. weakref.finalize()는 __del__()를 완전히 대체할까?
항상 그런 것은 아니다. 다만 Python 공식 문서는 __del__()보다 weakref.finalize()가 더 견고한 대안이 될 수 있다고 설명한다. 특히 cleanup에 필요한 최소 데이터만 분리해서 넘길 수 있다는 점이 실무적으로 크다.
Q. finalizer를 직접 호출하면 가비지 컬렉션 시에도 다시 실행될까?
아니다. 공식 문서 기준 finalizer는 최대 한 번만 콜백을 실행한다. 직접 호출해서 dead 상태가 되면 이후 객체가 수거돼도 콜백은 다시 실행되지 않는다.
Q. cleanup 함수 안에서 예외가 나면 try/except로 받을 수 있을까?
GC 중 실행된 콜백 예외는 표준 에러에 표시되지만 호출자에게 전파되지 않는다. 따라서 cleanup 실패를 서비스 로직의 예외 분기 조건으로 삼으려면 finalizer 대신 명시적 종료 함수를 별도로 두는 편이 낫다.
정리
weakref.finalize()는 객체 소멸 시점에 cleanup을 연결해야 하지만 __del__()에 기대고 싶지 않을 때 검토할 만한 표준 도구다. 핵심 기준은 세 가지다. 반환 객체를 보존하지 않아도 등록은 유지된다는 점, 기본적으로 종료 시점까지 살아 있는 finalizer도 처리된다는 점, 그리고 콜백이 obj를 다시 강하게 참조하면 안 된다는 점이다.
대부분의 코드에서는 즉시 종료가 필요한 리소스는 with나 명시적 close()로 처리하고, 보조 정리 훅이나 제3자 객체 생명주기 후처리에는 weakref.finalize()를 쓰는 구도가 무난하다.
참고 자료
'프로그래밍 > C, C++, Java, Python' 카테고리의 다른 글
| Python Queue.shutdown 사용법 (0) | 2026.07.13 |
|---|---|
| Python functools.Placeholder 사용법 (0) | 2026.07.10 |
| Python ExitStack 사용법 (0) | 2026.07.06 |
| Python asyncio timeout 기준 정리 (0) | 2026.07.01 |
| Python tarfile 추출 필터 기준 정리 (0) | 2026.06.25 |





