[Revised] property decorator that caches values

Rewrite the snippet I wrote earlier. Once read, the property will be retained until the object is destroyed. That's the specification.

You can also write to use a proper cache by specifying a cache backend for the decorator variable. Let's do it later

dec.py


from functools import wraps
import mox
from unittest import TestCase

def cached_property(f):
    prefix = '_cached_property_'
    @wraps(f)
    def _cached_property(self):
        key = prefix + f.__name__
        if hasattr(self, key):
            return getattr(self, key)
        value = f(self)
        setattr(self, key, value)
        return value
    return property(_cached_property)


class TestCachedProperty(TestCase):
    def setUp(self):
        self.m = mox.Mox()

    def tearDown(self):
        self.m.UnsetStubs()
        self.m = None

    def test_call_1(self):
        """Simple call."""

        # Procuder function that should be called twice.
        producer = self.m.CreateMockAnything()
        producer().AndReturn(30)
        producer().AndReturn(101)
        producer().AndReturn(None)
        producer().AndReturn(0)
        producer().AndReturn('')

        class C(object):
            @cached_property
            def f(self):
                return producer()

        # Try
        self.m.ReplayAll()
        c = C()
        c2 = C()
        c3 = C()
        c4 = C()
        c5 = C()

        # Verify
        self.assertEqual(c.f, 30)
        self.assertEqual(c.f, 30)
        self.assertEqual(c._cached_property_f, 30)
        self.assertEqual(c2.f, 101)
        self.assertEqual(c2.f, 101)
        self.assertEqual(c2._cached_property_f, 101)
        self.assertEqual(c3.f, None)
        self.assertEqual(c3.f, None)
        self.assertEqual(c3._cached_property_f, None)
        self.assertEqual(c4.f, 0)
        self.assertEqual(c4.f, 0)
        self.assertEqual(c4._cached_property_f, 0)
        self.assertEqual(c5.f, '')
        self.assertEqual(c5.f, '')
        self.assertEqual(c5._cached_property_f, '')
        self.m.VerifyAll()

        return

    def test_call_2(self):
        """Check that property function reference self."""

        # Procuder function that should be called once.
        producer = self.m.CreateMockAnything()
        producer().AndReturn(30)
        producer().AndReturn(101)

        class C(object):
            @cached_property
            def f(self):
                return self.g()
            def g(self):
                return producer()

        # Try
        self.m.ReplayAll()
        c = C()
        c2 = C()

        # Verify
        self.assertEqual(c.f, 30)
        self.assertEqual(c.f, 30)
        self.assertEqual(c._cached_property_f, 30)
        self.assertEqual(c2.f, 101)
        self.assertEqual(c2.f, 101)
        self.assertEqual(c2._cached_property_f, 101)
        self.m.VerifyAll()

        return

Recommended Posts

[Revised] property decorator that caches values
Summarize Python's @property decorator
Use a Property Decorator?