LazyProxy in Python

Posted by Michał ‘mina86’ Nazarewicz on 8th of July 2012

Paths of destiny lead mysterious ways. Not so long ago, I was a hard-core C hacker and now, I spend a lot of the time coding in Python.

In somehow related news, I have discovered that my search-foo is not good enough, when I was unable to find a decent implementations of several design patterns in Python.

What I needed was a generic proxy that would defer initialisation of an object to the moment it is first used. Here is what I came up with:

class LazyProxy(object):
    __slots__ = '__get'

    def __init__(self, cls, *args, **kw):
        object.__setattr__(self, '_LazyProxy__get',
                           lambda: self.__set(cls(*args, **kw)))

    def __set(self, obj):
        object.__setattr__(self, '_LazyProxy__get', lambda: obj)
        return obj

    def __getattr__(self, name):
        return getattr(self.__get(), name)

    def __setattr__(self, name, value):
        return setattr(self.__get(), name, value)

    def __delattr__(self, name):
        return delattr(self.__get(), name)

Here’s how one can use it:

class Foo(object):
    foo = 'foo'

    def __init__(self, bar, baz):
        print 'Creating Foo...'
        self.foo = 'foobar'
        self.bar = bar
        self.baz = baz

    def a(self):
        print 'Foo.a: self=(foo=%s, bar=%s, baz=%s)' % (
            self.foo, self.bar, self.baz)

    @classmethod
    def b(cls):
        print 'Foo.b: cls=%s, cls.foo=%s' % (
            cls.__name__, cls.foo)

    @staticmethod
    def c():
        print 'Foo.c'

foo = LazyProxy(Foo, 'bar', baz='qux')
print 'LazyProxy created'
foo.a()
foo.b()
foo.c()

foo can be used in (almost) the same way as an already created Foo object would. The only caveat is it will not be an instance of Foo which may or may not be an issue. Also, it is not thread safe.

Code © Google Inc.