Lazy evaluation

是一段源码,关于Lazy evaluation的,看了很久才懂,记录一下

 

一,lazy方法返回的比较复杂,一层一层将其剥开。

  1. wraps(func)跳转到curry(update_wrapper, func, WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES),最后return一个闭包,通过type(wraps(func))可以看到这是一个<function _curried>。
  2. wraps(func)(__wrapped)则会调用_curried(__wrapped__),进而调用update(func, __wrapped__ ,WRAPPER_ASSIGNMENTS, WRAPPER_UPDATE),这里注意WRAP这两个都是dict,所以会被收纳进**kwargs中。
  3. 通过update函数会把func的__module__,__name__,__doc__内容复制到<function __wrapper__>中,这样type(wrap(func)(__wrapper__))时,会返回一个<function func>。

这样lazy的返回就解析完了,这时候我们获得了一个__proxy__的构造函数。

  1 def curry(_curried_func, *args, **kwargs):
  2     def _curried(*moreargs, **morekwargs):
  3         return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
  4     return _curried
  5 
  6 WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
  7 WRAPPER_UPDATES = ('__dict__',)
  8 def update_wrapper(wrapper,
  9                    wrapped,
 10                    assigned = WRAPPER_ASSIGNMENTS,
 11                    updated = WRAPPER_UPDATES):
 12     """Update a wrapper function to look like the wrapped function
 13 
 14        wrapper is the function to be updated
 15        wrapped is the original function
 16        assigned is a tuple naming the attributes assigned directly
 17        from the wrapped function to the wrapper function (defaults to
 18        functools.WRAPPER_ASSIGNMENTS)
 19        updated is a tuple naming the attributes off the wrapper that
 20        are updated with the corresponding attribute from the wrapped
 21        function (defaults to functools.WRAPPER_UPDATES)
 22     """
 23     for attr in assigned:
 24         try:
 25             setattr(wrapper, attr, getattr(wrapped, attr))
 26         except TypeError: # Python 2.3 doesn't allow assigning to __name__.
 27             pass
 28     for attr in updated:
 29         getattr(wrapper, attr).update(getattr(wrapped, attr))
 30     # Return the wrapper so this can be used as a decorator via curry()
 31     return wrapper
 32 
 33 def wraps(wrapped,
 34           assigned = WRAPPER_ASSIGNMENTS,
 35           updated = WRAPPER_UPDATES):
 36     """Decorator factory to apply update_wrapper() to a wrapper function
 37 
 38        Returns a decorator that invokes update_wrapper() with the decorated
 39        function as the wrapper argument and the arguments to wraps() as the
 40        remaining arguments. Default arguments are as for update_wrapper().
 41        This is a convenience function to simplify applying curry() to
 42        update_wrapper().
 43     """
 44     return curry(update_wrapper, wrapped=wrapped,
 45                  assigned=assigned, updated=updated)
 46 class Promise(object):
 47     """
 48     This is just a base class for the proxy class created in
 49     the closure of the lazy function. It can be used to recognize
 50     promises in code.
 51     """
 52     pass
 53 
 54 def lazy(func, *resultclasses):
 55     """
 56     Turns any callable into a lazy evaluated callable. You need to give result
 57     classes or types -- at least one is needed so that the automatic forcing of
 58     the lazy evaluation code is triggered. Results are not memoized; the
 59     function is evaluated on every access.
 60     """
 61     class __proxy__(Promise):
 62         """
 63         Encapsulate a function call and act as a proxy for methods that are
 64         called on the result of that function. The function is not evaluated
 65         until one of the methods on the result is called.
 66         """
 67         __dispatch = None
 68 
 69         def __init__(self, args, kw):
 70             self.__func = func
 71             self.__args = args
 72             self.__kw = kw
 73             if self.__dispatch is None:
 74                 self.__prepare_class__()
 75 
 76         def __prepare_class__(cls):
 77             cls.__dispatch = {}
 78             for resultclass in resultclasses:
 79                 cls.__dispatch[resultclass] = {}
 80                 for (k, v) in resultclass.__dict__.items():
 81                     if hasattr(cls, k):
 82                         continue
 83                     setattr(cls, k, cls.__promise__(resultclass, k, v))
 84             cls._delegate_str = str in resultclasses
 85             cls._delegate_unicode = unicode in resultclasses
 86             assert not (cls._delegate_str and cls._delegate_unicode), "Cannot call lazy() with both str and unicode return types."
 87             if cls._delegate_unicode:
 88                 cls.__unicode__ = cls.__unicode_cast
 89             elif cls._delegate_str:
 90                 cls.__str__ = cls.__str_cast
 91         __prepare_class__ = classmethod(__prepare_class__)
 92 
 93         def __promise__(cls, klass, funcname, func):
 94             # Builds a wrapper around some magic method and registers that magic
 95             # method for the given type and method name.
 96             def __wrapper__(self, *args, **kw):
 97                 # Automatically triggers the evaluation of a lazy value and
 98                 # applies the given magic method of the result type.
 99                 res = self.__func(*self.__args, **self.__kw)
100                 for t in type(res).mro():
101                     if t in self.__dispatch:
102                         return self.__dispatch[t][funcname](res, *args, **kw)
103                 raise TypeError("Lazy object returned unexpected type.")
104 
105             if klass not in cls.__dispatch:
106                 cls.__dispatch[klass] = {}
107             cls.__dispatch[klass][funcname] = func
108             return __wrapper__
109         __promise__ = classmethod(__promise__)
110 
111         def __unicode_cast(self):
112             return self.__func(*self.__args, **self.__kw)
113 
114         def __str_cast(self):
115             return str(self.__func(*self.__args, **self.__kw))
116 
117         def __cmp__(self, rhs):
118             if self._delegate_str:
119                 s = str(self.__func(*self.__args, **self.__kw))
120             elif self._delegate_unicode:
121                 s = unicode(self.__func(*self.__args, **self.__kw))
122             else:
123                 s = self.__func(*self.__args, **self.__kw)
124             if isinstance(rhs, Promise):
125                 return -cmp(rhs, s)
126             else:
127                 return cmp(s, rhs)
128 
129         def __mod__(self, rhs):
130             if self._delegate_str:
131                 return str(self) % rhs
132             elif self._delegate_unicode:
133                 return unicode(self) % rhs
134             else:
135                 raise AssertionError('__mod__ not supported for non-string types')
136 
137         def __deepcopy__(self, memo):
138             # Instances of this class are effectively immutable. It's just a
139             # collection of functions. So we don't need to do anything
140             # complicated for copying.
141             memo[id(self)] = self
142             return self
143 
144     def __wrapper__(*args, **kw):
145         # Creates the proxy object, instead of the actual value.
146         return __proxy__(args, kw)
147 
148     return wraps(func)(__wrapper__)

  

二,我们现在学着怎么使用lazy。

  1. 这里我们新建了两个类,分别为A、B,其中B是A的子类。
  2. 当调用x = lazy(func, A, B)(1, 2)的时候,会返回一个__proxy__对象,这里注意,因为类的定义是在lazy方法中,所以__func为func。
  3. 这中间会调用__prepare_class__方法,这是一个类方法。他会从resultclasses(这里是A,B)中逐个选择,它维护一个类字典cls.__dispatch,对其中每一个类都把类的__dict__中不包括__main__、__name__、__doc__的属性都经过__promiss__闭包将__wrap__赋给__proxy__类。
  4. 在__wrap__执行之前,会在cls.__dispatch中添加一个名为funcname的属性为func。而当调用__wrap__的时候,则会使用func计算初始的*args和**kwargs,解析返回对象的类型列表。

这里要注意一点,任何一个类的__dict__是不会自动包括它父类的属性和方法的,所以最后结果,x.func_a()是会报错的。这应该是这段代码的一个不足或者BUG吧。

 

 1 def func(a, b):
 2     print 'func'
 3     return B()
 4 
 5 class A(object):
 6     """docstring for A"""
 7     def __init__(self):
 8         super(A, self).__init__()
 9     def func_a(self):
10         print 'in func_a'
11 
12 class B(object):
13     """docstring for B"""
14     def __init__(self):
15         super(B, self).__init__()
16     def func_b(self):
17         print 'in func_b'
18 
19 print B.mro()
20 x = lazy(func, A, B)(1, 2)
21 print x.func_a(), x.func_b()
1 #[<class '__main__.B'>, <class '__main__.A'>, <type 'object'>]
2 #func
3 #in func_b
4 #None

 

三,怎样实现lazy evaluation?

  将上面的代码改一下便可以实现lazy evaluation。

 1 def func(num1, num2):
 2     return B(num1, num2)
 3 
 4 class A(object):
 5     """docstring for A"""
 6     def __init__(self):
 7         super(A, self).__init__()
 8     def func_a(self):
 9         pass
10 
11 class B(A):
12     """docstring for B"""
13     def __init__(self, num1, num2):
14         super(B, self).__init__()
15         self.num1 = num1
16         self.num2 = num2
17 
18     def func_b(self, num1, num2):
19         print num1 + num2
20 
21 a = 1
22 b = 2
23 c = 3
24 x = lazy(func, A, B)(a, b)
25 a = b
26 b = c
27 print a, b, c
28 x.func_b(a, b)
29 
30 #2 3 3
31 #5

 

参考:http://blog.csdn.net/g9yuayon/article/details/759778

        http://blog.donews.com/superisaac/archive/2006/03/16/771387.aspx

posted @ 2013-09-16 16:19  vin_yan  阅读(779)  评论(0编辑  收藏  举报