Python caching generator
Memoizing a generator is a good idea to save having to rerun it. There are a few ways to do it - one is to handle it the way you're handling it, by returning the cached result if it exists, otherwise returning and caching the new result. 

The first way, is to the the tee() method from itertools. Such as follows:

from itertools import tee

def run():
    for i in range(1000):
        yield i

sequence, memoized_sequence = tee(run())


This returns a 32 byte object:

&lt;itertools.tee object at 0x23a564&gt;
&lt;itertools.tee object at 0x23a58c&gt;


which you can then iterate over as you normally would:

for x in memoized_sequence:
    print x


Another way to do it, if the generator consistently returns small sets of data, is to just use list(). This is especially true if, like aforementioned, the generator returns small sets of data and that data is not dependent on an action (ie, data is static) and that data is not kept in memory for a long time (such as in a child that is killed (and its memory freed) once its served its purpose). The way to do so, is:

def run():
    for i in range(1000):
        yield i

memoized_sequence = list(run())


However, this set of 1000 integers while have a size of 4.2KB in memory. So its not ideal, nor memory efficient, but its an option. 

Now, the way that you did it, as follows:

_results = []

def run():
    for i in range(1000):
        yield i

def results():
    def caching_iterator(it, r):
        for t in it:
            yield t
            r.append(t)

    if _results:  # _results is [] initially
        return _results
    return caching_iterator(run(), _results)


Would result in a 36 byte object:

&lt;generator object caching_iterator at 0x23f97c&gt;


I would suggest using the system provided tee function over the custom memoization technique, so your code would look like

from itertools import tee

def results():
    _, memoized_sequence = tee(self.run())
    return memoized_sequence


Doing so shaves you 4 bytes from the returned iterator object, as well as all the variable assignments and the added function and compacts your code.

EDIT

Seems that tee() does not memoize as I thought - it just returns an iterator. However, list() does so, though statically:

import random
from itertools import tee

def run():
    for i in range(5):
        x = random.randint(0, 100) * i
        print "calculating"
        yield x

_, memoized = tee(run())

for x in memoized:
    print x

l = list(run())
print l
print l


Results in:

calculating
0
calculating
92
calculating
94
calculating
165
calculating
100
calculating
calculating
calculating
calculating
calculating
[0, 43, 106, 222, 8]
[0, 43, 106, 222, 8]