Python 객체 정리(Cleanup)의 기본 원리
Python에서 전역 변수(globals)의 정리는 해당 변수를 None으로 설정하는 방식으로 수행됩니다. 반면 지역 변수(locals)는 세션이 종료되는 시점에 자동으로 소멸합니다. Python이 내부적으로 호출하는 __del__ 메서드는 전역 변수들을 None으로 설정하는 역할을 담당합니다.
객체 정리 예제 코드
다음 코드는 특정 클래스에 생성된 모든 객체를 정리하는 과정을 보여줍니다.
class Counter:
Count = 0 # 이 클래스로 생성된 객체의 총 개수
def __init__(self, name):
self.name = name
print(name, 'created')
Counter.Count += 1
def __del__(self):
print(self.name, 'deleted')
Counter.Count -= 1
if Counter.Count == 0:
print('Last Counter object deleted')
else:
print(Counter.Count, 'Counter objects remaining')
x = Counter("First")
del x
위 코드에서 마지막의 del 문을 제거하면 예외(exception)가 발생하게 됩니다.
Python 공식 문서의 __del__ 관련 경고
경고: __del__() 메서드는 호출되는 시점과 환경이 불안정할 수 있으므로, 실행 도중 발생하는 예외는 무시되며 대신 sys.stderr에 경고 메시지만 출력됩니다. 또한 모듈이 삭제되는 시점(예: 프로그램 실행이 끝나는 시점)에 __del__()이 호출되는 경우, 해당 메서드가 참조하던 다른 전역 변수들이 이미 삭제된 상태일 수 있습니다. 따라서 __del__() 메서드는 외부 불변성(external invariants)을 유지하는 데 필요한 최소한의 작업만 수행하도록 작성해야 합니다.
예외가 발생하는 이유
명시적인 del 호출 없이 프로그램을 종료하면 __del__은 프로그램 마지막에 호출됩니다. 하지만 그 시점에는 Counter 클래스와 Count 변수가 이미 가비지 컬렉터(GC)에 의해 수거되었을 가능성이 있습니다. 객체가 수거되는 순서는 결정적(deterministic)이지 않기 때문입니다. 즉, 발생하는 예외는 Counter가 이미 메모리에서 해제되었다는 의미이며, __del__ 안에서 복잡하거나 정교한 작업을 처리하는 것은 바람직하지 않습니다.
권장되는 두 가지 해결 방법
이러한 문제를 안전하게 해결하기 위해 다음 두 가지 방법을 사용할 수 있습니다.
- 명시적인 파이널라이저(finalizer) 메서드 사용 — 파일 객체에서 사용하는
close()메서드처럼, 리소스 해제가 필요한 시점에 직접 호출하는 정리 메서드를 구현합니다. - 약한 참조(weak reference) 사용 —
weakref모듈을 활용하면 객체의 생명 주기를 방해하지 않으면서도 객체가 소멸되었는지 확인하고 정리 로직을 안전하게 처리할 수 있습니다.