4 |
4 |
5 import functools |
5 import functools |
6 |
6 |
7 class LazyProperty (property) : |
7 class LazyProperty (property) : |
8 """ |
8 """ |
9 Lazy-loaded properties |
9 Lazy-loaded cached properties. |
|
10 |
|
11 This functions by overriding the class's property attribute with the actual value attribute in the instance's |
|
12 dict, causing all future attribute lookups to return that directly. |
10 """ |
13 """ |
11 |
|
12 ATTRNAME = '_LazyProperty_values' |
|
13 |
14 |
14 def __init__ (self, func) : |
15 def __init__ (self, func) : |
15 """ |
16 """ |
16 Initialize with no value |
17 Initialize with no value |
17 """ |
18 """ |
18 |
19 |
19 super(LazyProperty, self).__init__(func) |
20 super(LazyProperty, self).__init__(func) |
20 |
21 |
|
22 # the getter function |
21 self.func = func |
23 self.func = func |
|
24 |
|
25 # the attribute name |
22 self.name = func.__name__ |
26 self.name = func.__name__ |
23 |
|
24 self.value = None |
|
25 |
27 |
26 def run (self, obj) : |
28 def run (self, obj) : |
27 """ |
29 """ |
28 Run the background func and return the to-be-cached value |
30 Run the background func and return the to-be-cached value. |
|
31 |
|
32 `obj` is the object instance |
29 """ |
33 """ |
30 |
34 |
31 return self.func(obj) |
35 return self.func(obj) |
32 |
36 |
33 def obj_dict (self, obj): |
37 def store (self, obj, value) : |
34 """ |
38 """ |
35 Get the lazy-property dict of the given obj |
39 Store the cached value, overring ourself |
36 """ |
40 """ |
|
41 |
|
42 obj.__dict__[self.name] = value |
37 |
43 |
38 dict = getattr(obj, self.ATTRNAME, None) |
|
39 |
|
40 if dict is None : |
|
41 dict = {} |
|
42 |
|
43 setattr(obj, self.ATTRNAME, dict) |
|
44 |
|
45 return dict |
|
46 |
|
47 def get (self, obj) : |
|
48 """ |
|
49 Return the cached value |
|
50 """ |
|
51 |
|
52 return self.obj_dict(obj)[self.name] |
|
53 |
|
54 def set (self, obj, value) : |
|
55 """ |
|
56 Set the cached value |
|
57 """ |
|
58 |
|
59 self.obj_dict(obj)[self.name] = value |
|
60 |
|
61 def test (self, obj) : |
|
62 """ |
|
63 Tests if a value is set |
|
64 """ |
|
65 |
|
66 return self.name in self.obj_dict(obj) |
|
67 |
|
68 def __get__ (self, obj, owner) : |
44 def __get__ (self, obj, owner) : |
69 """ |
45 """ |
70 Return the cached value if it exists, otherwise, call the func |
46 Generate the value, store it as the attribute and return it. |
71 """ |
47 """ |
|
48 |
|
49 # compute value |
|
50 value = self.run(obj) |
72 |
51 |
73 if not obj : |
52 # store it |
74 return self |
53 self.store(obj, value) |
75 |
54 |
76 if not self.test(obj) : |
55 # return it |
77 # generate it |
56 return value |
78 self.set(obj, self.run(obj)) |
|
79 |
|
80 return self.get(obj) |
|
81 |
57 |
82 class LazyIteratorProperty (LazyProperty) : |
58 class LazyIteratorProperty (LazyProperty) : |
83 """ |
59 """ |
84 A lazy-loaded property that automatically converts an iterator/genexp into a list. |
60 A lazy-loaded property that automatically converts an iterator/genexp into a list. |
85 """ |
61 """ |