importweakrefclassResource:def__init__(self,name):self.name=namedefcallback(ref):print(f"Object was garbage collected!")obj=Resource("data")ref=weakref.ref(obj,callback)delobj# 출력: Object was garbage collected!
importweakrefclassExpensiveObject:def__init__(self,id):self.id=idprint(f"Created {id}")# 약한 참조 딕셔너리cache=weakref.WeakValueDictionary()defget_object(id):ifidnotincache:cache[id]=ExpensiveObject(id)returncache[id]obj1=get_object(1)# Created 1obj2=get_object(1)# 캐시에서 반환print(obj1isobj2)# Truedelobj1,obj2# 강한 참조가 없어지면 캐시에서 자동 제거print(dict(cache))# {}
4. WeakKeyDictionary - 객체에 데이터 연결
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
importweakrefclassUser:def__init__(self,name):self.name=name# 객체를 키로 사용하는 약한 참조 딕셔너리extra_data=weakref.WeakKeyDictionary()user=User("Alice")extra_data[user]={"score":100,"level":5}print(extra_data[user])# {'score': 100, 'level': 5}deluser# user 객체 삭제 시 extra_data에서도 자동 제거print(dict(extra_data))# {}
importweakrefclassData:def__init__(self,value):self.value=valuedefget(self):returnself.valueobj=Data(42)proxy=weakref.proxy(obj)# ref()와 달리 직접 사용 가능print(proxy.value)# 42print(proxy.get())# 42delobj# 이제 접근하면 ReferenceError# print(proxy.value) # ReferenceError: weakly-referenced object no longer exists
7. finalize - 정리 콜백
1
2
3
4
5
6
7
8
9
10
11
12
13
14
importweakrefclassResource:def__init__(self,name):self.name=namedefcleanup(name):print(f"Cleaning up {name}")obj=Resource("data")weakref.finalize(obj,cleanup,obj.name)delobj# 출력: Cleaning up data
importweakrefclassParent:def__init__(self,name):self.name=nameself.children=[]classChild:def__init__(self,name,parent):self.name=name# 약한 참조로 순환 참조 방지self._parent_ref=weakref.ref(parent)@propertydefparent(self):returnself._parent_ref()parent=Parent("Parent")child=Child("Child",parent)parent.children.append(child)# 순환 참조가 있어도 gc가 정상 작동print(child.parent.name)# Parent
importweakreffromfunctoolsimportlru_cacheclassCachedLoader:_cache=weakref.WeakValueDictionary()@classmethoddefload(cls,key):obj=cls._cache.get(key)ifobjisNone:obj=cls._expensive_load(key)cls._cache[key]=objreturnobj@staticmethoddef_expensive_load(key):print(f"Loading {key}...")return{"data":key}# 사용data1=CachedLoader.load("key1")# Loading key1...data2=CachedLoader.load("key1")# 캐시에서 반환# 강한 참조 유지 중에는 캐시 유지deldata1data3=CachedLoader.load("key1")# 아직 data2가 있어서 캐시 히트
약한 참조 불가능한 타입
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
importweakref# 약한 참조 불가능# weakref.ref(42) # TypeError# weakref.ref("string") # TypeError# weakref.ref([1, 2]) # TypeError# weakref.ref((1, 2)) # TypeError# 약한 참조 가능classMyClass:passweakref.ref(MyClass())# OK# __slots__가 있으면 __weakref__ 포함 필요classSlotted:__slots__=['value','__weakref__']
자주 하는 실수
1. 약한 참조 결과 확인 없이 사용
1
2
3
4
5
6
7
8
9
10
11
importweakrefref=weakref.ref(some_object)# 위험: 객체가 삭제되었을 수 있음# ref().method()# 안전: None 체크obj=ref()ifobjisnotNone:obj.method()
2. 임시 객체에 약한 참조
1
2
3
4
5
6
7
8
9
importweakref# 잘못된 사용: 임시 객체는 바로 사라짐ref=weakref.ref(SomeClass())print(ref())# None (이미 삭제됨)# 올바른 사용: 강한 참조 유지obj=SomeClass()ref=weakref.ref(obj)