2017-12-22 6 views
2

객체가 있고 그것의 메소드 중 하나가 PyQt 신호가 방출 될 때 실행되기를 원한다고 가정합니다. 그리고 신호로 전달되지 않는 매개 변수로 그렇게하기를 원한다고 가정합니다. 그래서 신호의 슬롯으로 람다 작성 : 일반적으로 PyQt는 신호와 슬롯, 이제람다에있는 객체의 수명이 pyqtSignal에 연결됨

class MyClass(object): 
    def __init__(self, model): 
     model.model_changed_signal.connect(lambda: self.set_x(model.x(), silent=True)) 

를, 신호 연결은 가비지 수집을 방지하지 않습니다. 연결된 슬롯의 객체가 가비지 수집되면 해당 신호가 방출 될 때 더 이상 슬롯이 호출되지 않습니다.

그러나 람다를 사용할 때 어떻게 작동합니까? 나는 람다에 대한 참조를 저장하지 않지만 신호 슬롯 연결은 계속 작동합니다. 따라서 람다는 가비지 수집되지 않습니다.

MyClass의 인스턴스를 None으로 설정하면 해당 인스턴스가 가비지 수집되지 않습니다. model_changed_signal을 방출해도 여전히 람다가 실행됩니다. 그래서 분명히 MyClass의 인스턴스에 대한 참조가 어딘가에있을 것입니다 (어쩌면 람다의 맥락에서)? 나는 원하지 않습니다.

왜 이런 일이 발생합니까?

답변

2

예제의 lambda은 클로저를 구성합니다. 즉, 해당 범위에서 사용할 수있는 객체를 참조하는 중첩 함수입니다. 클로저를 만드는 모든 함수는 참조를 유지하는 데 필요한 모든 항목에 대해 cell object을 유지합니다.

예에서 lambda__init__ 메서드의 범위 내에있는 로컬 selfmodel 변수에 대한 참조가있는 클로저를 만듭니다. lambda 어딘가에 대한 참조를 유지하면 __closure__ 속성을 통해 클로저의 모든 셀 객체를 검사 할 수 있습니다. 당신이 여기에 표시된 MyModelMyClass 개체 것이 여전히 남아있는 세포에 의해 유지 것과 다른 모든 참조를 삭제 한 경우

>>> print(func.__closure__) 
(<cell at 0x7f99c16c5138: MyModel object at 0x7f99bbbf0948>, <cell at 0x7f99c16c5168: MyClass object at 0x7f99bbb81390>) 

: 당신의 예에서,이 같은 것을 표시하는 것입니다. 따라서 객체 정리에 관해서는, 관련 객체에 대해 클로저를 형성 할 수있는 함수에 연결된 모든 신호를 항상 명시 적으로 연결 해제해야합니다. 이/신호 슬롯 커넥션에 관해서, PyQt는 다르게 처리 C++ 슬롯 파이썬 인스턴스 메소드를 감싸 것을


참고. 호출 가능 유형의 참조 카운트는 신호에 연결될 때 이 아닌이 증가하는 반면 람다, 정의 된 함수, 부분 객체 및 정적 메소드는 증가합니다. 즉, 후자 유형의 호출 가능 항목에 대한 다른 모든 참조가 삭제 된 경우 나머지 신호 연결이 해당 호출을 계속 유지합니다. 필요한 경우 신호를 연결 해제하면 이러한 연결된 통화 대상을 가비지 수집 할 수 있습니다.

위의 예외 중 하나는 클래스 메소드입니다. PyQt는 이러한 연결을 만드는 특별한 래퍼를 작성하므로 그들에게 다른 모든 참조가 삭제 된 경우, 신호는 다음과 같이 예외가 발생합니다, 방출 :

TypeError: 'managedbuffer' object is not callable 

은 위의 PyQt5에 적용해야하며, 대부분의 PyQt4 버전 (4.3 이상).

+0

람다가 MyClass 및 MyModel에 대한 참조를 유지하고 있음을 확인하고 명확히합니다. 감사합니다!내 남은 불명예는 lambda의 가비지 수집이 다른 함수 객체와 다른 이유입니다. 클래스를 인스턴스화하고 메소드 중 하나를'model.model_changed_signal.connect (ModelListener(). handle_signal)'과 연결하면'ModelListener'의 인스턴스가 가비지 컬렉팅되고'handle_signal'은 결코 사라지지 않을 것입니다. 라는. – tjalling

+0

@tjalling. 쓰레기 수렴은 람다와 똑같습니다. 그 차이점은 전적으로 클로저 때문이며, * 모든 * 함수는 그 중 하나를 형성 할 수 있습니다. 함수가 아니라 참조를 유지하는 클로저입니다. 귀하의 의견에 주어진 예문에는 종결 조항이 없습니다. – ekhumoro

+0

하지만 클로저에 대한 참조가 필요하지 않습니까? 람다에 대한 마지막 참조를 버리면 (예 : 'None'으로 설정) 람다와 해당 클로저가 가비지 수집됩니다. 맞습니까? 왜 람다를 'pyqtSignal'에 연결하면 람다 (그리고 클로저)가 쓰레기 수집되는 것을 막을 수있는 반면, 바운드 함수를 신호에 연결한다고해서 함수가 바인딩 된 객체가 가비지 수집되는 것을 막을 수는 없습니다. 모은? – tjalling