2010-02-19 2 views
3

순환 참조가있는 Python 클래스의 종료자를 작성하려고합니다. 약한 참조 콜백이 way to go이라는 것을 알았습니다. 불행히도 콜백이 호출되지 않기 때문에 내가 사용하는 람다처럼 보인다. (: 인쇄 'A1 알라 삭제') 람다 콜백 작품순환 참조로 인해 약한 참조 콜백이 호출되지 않습니다.

A created 
B created 

순환 참조 분리 만든다 :

def del_A(name): 
    print('An A deleted:' + name) 

class A(object): 
    def __init__(self, name): 
     print('A created') 
     self.name = name 
     self._wr = weakref.ref(self, lambda wr, n = self.name: del_A(n)) 

class B(object): 
    def __init__(self): 
     print('B created') 

if __name__ == '__main__': 
    a = A('a1') 
    b = B() 
    a.other = b 
    b.other = a 

결과 : 예를 들어,이 코드를 실행.

self._wr = weakref.ref(self, del_A(self.name)) 
... 
a = A('a1') 
a.name = 'a2' 
b = B() 
a.other = b 
b.other = a 

반환 : 콜백 호출 할 때 약한 참조를 초기화하고,하지 않을 때 간단한 함수 호출에 의해 람다 교체도 작동하지만 매개 변수 값은 고정되어

A created 
An A deleted:a1 
B created 

어떤 생각을 왜 람다 콜백이 순환 참조와 함께 작동하지 않습니까?

답변

2

는 내가 마지막으로 콜백이 약한 참조의 존재에 호출되지 않은 이유를 찾은 것 같아 :

약한 참조 콜백이 (가) "weakref object dies before the object it references"

것 같다 경우 호출되지 않습니다 순환 참조가 삭제 될 때 콜백이 호출되기 전에 클래스 A의 약한 참조 속성이 삭제됩니다. 한 가지 해결책은 파이널 라이저 (finalizer) (즉, 약한 참조 및 콜백)를 파이널 라이저 목록에 추가하는 것입니다.예를 들어 :

def del_A(name): 
    print('An A deleted:' + name) 

class A(object): 
    def __init__(self, name, finalizers): 
     print('A created') 
     self.name = name 
     finalizers.append(weakref.ref(self, lambda wr, n = self.name: del_A(n))) 

class B(object): 
    def __init__(self): 
     print('B created') 

def do_work(finalizers): 
    a = A('a1', finalizers) 
    b = B() 
    a.other = b 
    b.other = a 

if __name__ == '__main__': 
    finalizers = [] 
    do_work(finalizers) 

인쇄됩니다 :

A created 
B created 
An A deleted:a1 

참고 do_work()를 필요가있다, 그렇지 않으면 파이 나라는 삭제됩니다 콜백이 호출 할 수있는 기회를하기 전에. 분명히 약한 참조 목록을 만드는 것을 피하기 위해 finalizer를 제대로 관리해야하지만 또 다른 문제입니다.

0

순환 참조가 자동으로 정리됩니다. __del__ 메서드를 정의하는 클래스와 같은 몇 가지 예외가 있습니다. 당신이

self._wr = weakref.ref(self, lambda wr, n = self.name: del_A(n)) 

self가 완료하려고 할 때 콜백 만 호출됩니다를 사용하는 경우

보통 당신은 __del__ 방법

+0

답장을 보내 주셔서 감사합니다. 그러나 문제가 해결되지 않는다고 생각합니다. 나는 약한 참조를 사용하여 __del__ 메소드를 피한다. – Barthelemy

2

을 정의 할 필요가 없습니다.

a = A('a1') 
b = B() 
a.other = b # This gives a another attribute; it does not switch `a` away from the original `a` 
b.other = a 

a이 확정되도록하지 않기 때문에 콜백이 호출 받고되지 않는 이유

이유입니다. 원본 a이 아직 존재합니다. 당신이

a = A('a1') 
b = B() 
a = b 
b = a 

에 코드를 변경 한 경우 당신은 당신의 콜백 None입니다 다음

self._wr = weakref.ref(self, del_A(self.name)) 

를 사용하는 경우

콜백

호출 할 것이다. del_A(self.name)은 함수에 대한 참조가 아니며 함수 호출 자체입니다. 따라서 del_A(self.name)An A deleted:a1을 즉시 인쇄하고 ( a1이 실제로 완료되기 전에) 약한 참조에 대한 기본 콜백이되는 None 값을 반환합니다.

+0

답변 해 주셔서 감사합니다. 나는 내 질문의 말씨를 분명히해야한다. 나는 a.other = b가 a를 전환하지 않는다는 것을 알고 있지만, 코드 블록의 끝에 a와 b가 범위를 벗어나서 가비지 수집/마무리 될 것으로 기대합니다. 그러면 람다 콜백이 호출되지 않습니다. 순환 참조를 제거하면 람다 콜백이 호출되어 나를 괴롭힌다. – Barthelemy

+0

하지만 self._wr = weakref.ref (self, del_A (self.name))가 작동하지 않는 이유에 대한 설명이 옳습니다. del_A()는 참조가 아닌 함수 호출이므로 콜백을 전달하는 적절한 방법이 아닙니다. 감사. – Barthelemy

관련 문제