Welcome, guest | Sign In | My Account | Store | Cart

Without thinking in thread creation the idea is to call several times a function assigning a thread for each call with related parameters and returning the list of results in a pretty pythonic way. For example, if we have already defined the function 'func': res = func(par) we want to call that several times and get the values in the future. So: func(par1) func(par2) func(par3).get() func(par4).get_all()

assigning a thread for each call. The 'get' method returns the first possible results, and 'get_all' returns all the values in a list. The decorator works fine with kwargs too.

This recipe is based on: http://code.activestate.com/recipes/355651-implementing-futures-with-decorators/ that use only one call for the function. The problem in that recipe is that each call blocks the execution.

Vote if you like it!

Python, 115 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
from threading import Thread, Event

class future:
    """
    Without thinking in thread creation the idea is to call several times 
    a function assigning a thread for each call with related parameters 
    and returning the list of results in a pretty pythonic way.
    For example, if we have already defined the function 'func':
    res = func(par)
    we want to call that several times and get the values in the future. So: 
    func(par1)
    func(par2)
    func(par3).get()
    func(par4).get_all()
    
    assigning a thread for each call. The 'get' method returns the first possible results,
    and 'get_all' returns all the values in a list. The decorator works fine with kwargs too.
    
    This recipe is based on:
    http://code.activestate.com/recipes/355651-implementing-futures-with-decorators/
    that use only one call for the function. The problem in that recipe is that each call blocks the execution.
    """
    def __init__(self, f):
        self.__f = f
        self.__init_values__()
    
    def __init_values__(self):
        self.__thread = []
        self.__vals = []
        self.__exception = None
        self.__curr_index = 0
        self.__event = Event()
        
    def __call__(self, *args, **kwargs):
        t = Thread(target=self.runfunc, args=args, kwargs=kwargs)
        t.start()
        self.__thread.append(t)
        return self
    
    def runfunc(self, *args, **kw):
        try:
            self.__vals.append(self.__f(*args, **kw))
            self.__event.set()
        except Exception, e:
            self.__exception = e

    def get(self):
        """
        Returns the first possible value without order of calling the function.
        """
        if self.__curr_index == len(self.__thread):
            raise IndexError('The element doesn\'t exists')
        if not self.__event.is_set():
            self.__event.wait()
        self.__event.clear()
        res = self.__vals[self.__curr_index]
        self.__curr_index += 1
        return res

    
    def get_all(self):
        """
        Returns all the possible values and initialize them.
        """
        for t in self.__thread:
            t.join()
        
        if self.__exception is not None:
            raise self.__exception

        res = self.__vals
        
        # Get rid of everything
        self.__init_values__()
        
        return res

    

if __name__ == '__main__':
    import time
    import unittest
    

    
    @future
    def sleeping(s, t):
        time.sleep(s)
        return 'slept for '+str(s) + ' sec dreaming: ' + str(t)
    
    sleeping(2, t='world')
    sleeping(5, 'sheeps')
    sleeping(1, t='soccer')
    print(sleeping(2, t='women').get())
    print(sleeping(1, 'beach').get_all())
    
    class FutureTestCase(unittest.TestCase):
        def tearDown(self):
            sleeping(0, '').get_all()
            
        def test_positive(self):
            sleeping(5, t='sheeps')
            sleeping(1, 'beach')
            o = sleeping(3, 'nothing')
            res = o.get_all()
            self.assertEqual(3, len(res))

        def test_bad_index(self):
            sleeping(5, t='sheeps')
            o = sleeping(4, 'world')
            o.get()
            o.get()
            self.assertRaises(IndexError, o.get)
            
    unittest.main(verbosity=2)
    
    

The output will be:

slept for 1 sec dreaming: soccer
['slept for 1 sec dreaming: soccer', 'slept for 2 sec dreaming: world', 'slept for 2 sec dreaming: women', 'slept for 1 sec dreaming: beach', 'slept for 5 sec dreaming: sheeps']
test_bad_index (__main__.FutureTestCase) ... ok
test_positive (__main__.FutureTestCase) ... ok

----------------------------------------------------------------------
Ran 2 tests in 10.011s

OK