파이썬 클린코드 발표 디스크립터 종류

HHHHH·2021년 8월 8일
0

개념정리

목록 보기
4/6

디스크립터의 유형

__set__이나 __delete__ 메서드를 구현했다면 데이터 디스크립터(data descriptor)

__get__만 구현했다면 비데이터 디스크립터

둘의 차이는?

데이터 디스크립터는 객체의 사전(__dict__)보다 우선적으로 적용

비데이터 디스크립터는 객체의 객체의 사전 값이 우선 적용

데이터 디스크립터


class DataDescriptor:


    def __get__(self, instance, owner):
        if instance is None:
            return self
        return 42

    def __set__(self, instance, value):
        logger.debug("setting %s.descriptor to %s", instance, value)
        instance.__dict__["descriptor"] = value


class ClientClass:
	descripor = DataDescriptor()
  
	
## 실제 descriptor의 반환값이 어떻게 되는 지 확인해보기 

>>> client = ClientClass()
>>> client.descriptor
42 # \_\_get__을 통해서 42 반환 
>>> client.descriptor = 99 
>>> vars(client)
{'descriptor': 99}
>>> client.__dict__["descriptor"]
99
# client.descripotr에 99을 할당하면 client 객체의 __dict__는 업데이트가 됨 
>>> client.descriptor
42
# 하지만 descriptor의 반환값은 변하지않고 42임 
# -> 데이터 디스크립터는 인스턴스의 __dict__을 오버라이드 해서 
# 인스턴스 __dict__보다 높은 우선 순위를 가지기 때문 
>>> del client.descriptor
'''
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
AttributeError: __delete__
'''

# 속성 삭제 역시 동작하지않음. client.descriptor를 삭제하면 
# 인스턴스의 __dict__을 지우는게 아니라 descriptor에서 
# __delete__() 메서드를 호출하는데 __delete__가 구현되어 있지 않기 때문임.

정리

디스크립터에 __set__() 메서드를 구현했다면(데이터 디스크립터) 객체의 사전보다 우선 순위를 가진다.
디스크립터가 __set__() 메서드를 구현하지 않았다면(비데이터 디스크립터) 객체의 사전이 우선순위를 갖고, 디스크립터가 실행된다.

위 코드에서 __set__ 파트 고찰

instance.__dict__["descriptor"] = value

디스크립터의 이름을 설정하지 않았기때문에 "descriptor"라는 이름의 속성 값을 바꿔줌
__init__ 메서드에서 디스크립터 이름을 받아서 내부에 저장하거나
__set_name__ 메서드를 사용해 이름을 설정할 수 있음.

인스턴스의 __dict__에 직접 접근하지않고 setter나 instance.descriptor = value로 바로 할당하면 무한 루프가 발생함.
setattr()을 사용하면 디스크립터의 __set__ 메서드가 호출되고, __set__메서드는 setattr을 호출하고 다시 __set__을 호출하기 때문

디스크립터가 모든 인스턴스의 프로퍼티 값을 보관할 수 없는 이유는?

('모든' 인스턴스에서 '모든'이라고 쓴 이유를 모르겠음.)(프로퍼티 값이라는게 이해안됨. get, set을 의미하는건지 )

클라이언트 클래스는 이미 디스크립터의 참조를 가지고 있음.
디스크립터가 다시 클라이언트 객체를 참조하면 순환 종속성(cicular dependencies)이 생겨 가비지 컬렉션이 되지않음.
서로를 가르키고 있기 때문에 참조 카운트(reference counts)가 제거 임계치 이하로 떨어지지 않음.

sys.getrefcount()을 통해 확인가능.
가비지 컬렉션에 대해 자세히 알려면 링크

-> weakref 모듈에 있는 약한 참조를 사용하여 약한 참조 키 사전을 만드는 것이 일반적인 대안

profile
공부중

0개의 댓글