[오픈소스에서 배우다] Django의 cached_property
파이썬을 대표하는 웹프레임워크 중 하나인 Django
에는 다음과 같은 코드가 있다. (코드 직접 확인하기)
class cached_property(object):
"""
Decorator that converts a method with a single self argument into a
property cached on the instance.
Optional ``name`` argument allows you to make cached properties of other
methods. (e.g. url = cached_property(get_absolute_url, name='url') )
"""
def __init__(self, func, name=None):
self.func = func
self.__doc__ = getattr(func, '__doc__')
self.name = name or func.__name__
def __get__(self, instance, cls=None):
if instance is None:
return self
res = instance.__dict__[self.name] = self.func(instance)
return res
번역하면, "self
만을 argument로 가지고 있는 인스턴스의 메소드를 인스턴스에 캐싱되는 속성(Property)으로 만들어주는 클래스 데코레이터"이다. (클래스도 데코레이터로 사용할 수 있다.)
예를 들어 아래와 같은 클래스를 정의하고 called_count
라는 메소드를 정의해보자.
class TestClass(object):
def __init__(self):
self.called = 0
@cached_property
def called_count(self):
self.called += 1
return self.called
아래와 같이 인스턴스를 만들어서 called_count
를 실행해보면 처음 호출시에만 called
가 +1이 되고 그 이후부터는 해당 값을 캐싱하여 별도의 연산없이 캐싱된 값을 리턴한다.
t = TestClass()
print(t.called) # >> 0
print(t.called_count) # >> 1
print(t.called_count) # >> 1
print(t.called_count) # >> 1
TestClass
클래스가 선언되면서 cached_property
클래스의 __init__
메소드가 실행되고 TestClass
의 called_count
메소드가 cached_property
클래스에 등록된다. (메소드 뿐만이아니라 메소드의 docstring, 그리고 name까지 등록된다.) 처음 called_count
를 부를 때 cached_property
의 __get__
매직 메소드를 호출하고, TestClass의 인스턴스 (여기서는 t
)의 __dict__
에 'key'로 called_count, 'value'로 called_count
메소드를 실행한 값을 담는다. 그 이후 called_count
를 호출할 때는 cached_property
의__get__
을 거치지않고 t
의 __dict__
에 저장된 값을 반환한다.
그렇다면 cached_property
를 사용하면 어떤 장점이 있을까? 복잡한 연산이나 네트워크 IO를 통해 얻을 수 있는 속성값의 경우 해당 속성을 사용하지 않는 경우 굉장히 큰 비용이 발생하는데 이를 방지할 수 있으며, 해당 속성값을 여러번 호출하여야 하는 경우에도 결과값이 캐싱이 되어있기 때문에 반복적으로 연산이나 네트워크 IO를 거치며 발생하는 딜레이를 줄여 프로그램을 보다 효율적으로 만들 수 있다.
'오픈소스에서 배우다'는 오픈소스 코드들을 읽으면서 스스로 배우고 싶은 것들이나, 나누고 싶은 것들을 공유하는 시리즈입니다.