Lazy evaluation (bottle_utils.lazy)

This module provides classes and decorators for lazy evaluation of callables.

Lazily evaluated functions and methods return immediately without performing any work, and their work is performed at some later point in time when the results are actually needed. This is useful in situation when the result may or may not be needed (e.g., some branching may occur) or when evaluation of the result may not be possible at the time of a call but we know it will be possible later.

In Bottle Utils, lazy evaluation is used extensively in the i18n module. Because evaluating the translation context requires a request context, and it may not be available where the call happens, lazy evaluation postpones evaluation of the translation function until we are sure the request context exists (e.g., some translated string is stored as a defined constant somewhere and used later in the request handler).

How it works

When a function is wrapped in a Lazy object, it behaves like result of evaluating that function. It stores the function in one of its properties and waits for your code to actually try to use the result. If your code never uses the result, the wrapped function is never called. When your code uses the result, the function is then evaluated for the first time. This type of object is sometimes also referred to as a proxy.

The idea of ‘using’ the result is defined as an attempt to coerce or perform any action that triggers any of the magic methods. This includes things like adding or subtracting from another value, calling str(), bool() and similar methods, attempting string interpolation with either format() method or % operator, accessing indices or keys using subscript notation, and so on. Attempting to access methods and properties also counts as ‘using’.

One caveat of this behavior is that, because lazy functions are created in one context and potentially evaluated in another, the state in which they are evaluated may change in unpredictable ways. On the other hand, this may create opportunities that would not exist without lazy evaluation.

More on lazy evaluation in general can be found in Content Creation Wiki.

Module contents

class bottle_utils.lazy.Lazy(_func, *args, **kwargs)[source]

Lazy proxy object. This proxy always evaluates the function when it is used.

Any positional and keyword arguments that are passed to the constructor are stored and passed to the function except the _func argument which is the function itself. Because of this, the wrapped callable cannot use an argument named _func itself.

class bottle_utils.lazy.CachingLazy(_func, *args, **kwargs)[source]

Caching version of the Lazy class. Unlike the parent class, this class only evaluates the callable once, and remembers the resutls. On subsequent use, it returns the original result. This is probably closer to the behavior of a normal return value.

bottle_utils.lazy.lazy(fn)[source]

Convert a function into lazily evaluated version. This decorator causes the function to return a Lazy proxy instead of the actual results.

Usage is simple:

@lazy
def my_lazy_func():
    return 'foo'
bottle_utils.lazy.caching_lazy(fn)[source]

Convert a function into cached lazily evaluated version. This decorator modifies the function to return a CachingLazy proxy instead of the actual result.

This decorator has no arguments:

@caching_lazy
def my_lazy_func():
    return 'foo'