Lazy evaluation
是一段源码,关于Lazy evaluation的,看了很久才懂,记录一下
一,lazy方法返回的比较复杂,一层一层将其剥开。
- wraps(func)跳转到curry(update_wrapper, func, WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES),最后return一个闭包,通过type(wraps(func))可以看到这是一个<function _curried>。
- wraps(func)(__wrapped)则会调用_curried(__wrapped__),进而调用update(func, __wrapped__ ,WRAPPER_ASSIGNMENTS, WRAPPER_UPDATE),这里注意WRAP这两个都是dict,所以会被收纳进**kwargs中。
- 通过update函数会把func的__module__,__name__,__doc__内容复制到<function __wrapper__>中,这样type(wrap(func)(__wrapper__))时,会返回一个<function func>。
这样lazy的返回就解析完了,这时候我们获得了一个__proxy__的构造函数。
def curry(_curried_func, *args, **kwargs):
def _curried(*moreargs, **morekwargs):
return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
return _curried WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Update a wrapper function to look like the wrapped function wrapper is the function to be updated
wrapped is the original function
assigned is a tuple naming the attributes assigned directly
from the wrapped function to the wrapper function (defaults to
functools.WRAPPER_ASSIGNMENTS)
updated is a tuple naming the attributes off the wrapper that
are updated with the corresponding attribute from the wrapped
function (defaults to functools.WRAPPER_UPDATES)
"""
for attr in assigned:
try:
setattr(wrapper, attr, getattr(wrapped, attr))
except TypeError: # Python 2.3 doesn't allow assigning to __name__.
pass
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr))
# Return the wrapper so this can be used as a decorator via curry()
return wrapper def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Decorator factory to apply update_wrapper() to a wrapper function Returns a decorator that invokes update_wrapper() with the decorated
function as the wrapper argument and the arguments to wraps() as the
remaining arguments. Default arguments are as for update_wrapper().
This is a convenience function to simplify applying curry() to
update_wrapper().
"""
return curry(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
class Promise(object):
"""
This is just a base class for the proxy class created in
the closure of the lazy function. It can be used to recognize
promises in code.
"""
pass def lazy(func, *resultclasses):
"""
Turns any callable into a lazy evaluated callable. You need to give result
classes or types -- at least one is needed so that the automatic forcing of
the lazy evaluation code is triggered. Results are not memoized; the
function is evaluated on every access.
"""
class __proxy__(Promise):
"""
Encapsulate a function call and act as a proxy for methods that are
called on the result of that function. The function is not evaluated
until one of the methods on the result is called.
"""
__dispatch = None def __init__(self, args, kw):
self.__func = func
self.__args = args
self.__kw = kw
if self.__dispatch is None:
self.__prepare_class__() def __prepare_class__(cls):
cls.__dispatch = {}
for resultclass in resultclasses:
cls.__dispatch[resultclass] = {}
for (k, v) in resultclass.__dict__.items():
if hasattr(cls, k):
continue
setattr(cls, k, cls.__promise__(resultclass, k, v))
cls._delegate_str = str in resultclasses
cls._delegate_unicode = unicode in resultclasses
assert not (cls._delegate_str and cls._delegate_unicode), "Cannot call lazy() with both str and unicode return types."
if cls._delegate_unicode:
cls.__unicode__ = cls.__unicode_cast
elif cls._delegate_str:
cls.__str__ = cls.__str_cast
__prepare_class__ = classmethod(__prepare_class__) def __promise__(cls, klass, funcname, func):
# Builds a wrapper around some magic method and registers that magic
# method for the given type and method name.
def __wrapper__(self, *args, **kw):
# Automatically triggers the evaluation of a lazy value and
# applies the given magic method of the result type.
res = self.__func(*self.__args, **self.__kw)
for t in type(res).mro():
if t in self.__dispatch:
return self.__dispatch[t][funcname](res, *args, **kw)
raise TypeError("Lazy object returned unexpected type.") if klass not in cls.__dispatch:
cls.__dispatch[klass] = {}
cls.__dispatch[klass][funcname] = func
return __wrapper__
__promise__ = classmethod(__promise__) def __unicode_cast(self):
return self.__func(*self.__args, **self.__kw) def __str_cast(self):
return str(self.__func(*self.__args, **self.__kw)) def __cmp__(self, rhs):
if self._delegate_str:
s = str(self.__func(*self.__args, **self.__kw))
elif self._delegate_unicode:
s = unicode(self.__func(*self.__args, **self.__kw))
else:
s = self.__func(*self.__args, **self.__kw)
if isinstance(rhs, Promise):
return -cmp(rhs, s)
else:
return cmp(s, rhs) def __mod__(self, rhs):
if self._delegate_str:
return str(self) % rhs
elif self._delegate_unicode:
return unicode(self) % rhs
else:
raise AssertionError('__mod__ not supported for non-string types') def __deepcopy__(self, memo):
# Instances of this class are effectively immutable. It's just a
# collection of functions. So we don't need to do anything
# complicated for copying.
memo[id(self)] = self
return self def __wrapper__(*args, **kw):
# Creates the proxy object, instead of the actual value.
return __proxy__(args, kw) return wraps(func)(__wrapper__)
二,我们现在学着怎么使用lazy。
- 这里我们新建了两个类,分别为A、B,其中B是A的子类。
- 当调用x = lazy(func, A, B)(1, 2)的时候,会返回一个__proxy__对象,这里注意,因为类的定义是在lazy方法中,所以__func为func。
- 这中间会调用__prepare_class__方法,这是一个类方法。他会从resultclasses(这里是A,B)中逐个选择,它维护一个类字典cls.__dispatch,对其中每一个类都把类的__dict__中不包括__main__、__name__、__doc__的属性都经过__promiss__闭包将__wrap__赋给__proxy__类。
- 在__wrap__执行之前,会在cls.__dispatch中添加一个名为funcname的属性为func。而当调用__wrap__的时候,则会使用func计算初始的*args和**kwargs,解析返回对象的类型列表。
这里要注意一点,任何一个类的__dict__是不会自动包括它父类的属性和方法的,所以最后结果,x.func_a()是会报错的。这应该是这段代码的一个不足或者BUG吧。
def func(a, b):
print 'func'
return B() class A(object):
"""docstring for A"""
def __init__(self):
super(A, self).__init__()
def func_a(self):
print 'in func_a' class B(object):
"""docstring for B"""
def __init__(self):
super(B, self).__init__()
def func_b(self):
print 'in func_b' print B.mro()
x = lazy(func, A, B)(1, 2)
print x.func_a(), x.func_b()
#[<class '__main__.B'>, <class '__main__.A'>, <type 'object'>]
#func
#in func_b
#None
三,怎样实现lazy evaluation?
将上面的代码改一下便可以实现lazy evaluation。
def func(num1, num2):
return B(num1, num2) class A(object):
"""docstring for A"""
def __init__(self):
super(A, self).__init__()
def func_a(self):
pass class B(A):
"""docstring for B"""
def __init__(self, num1, num2):
super(B, self).__init__()
self.num1 = num1
self.num2 = num2 def func_b(self, num1, num2):
print num1 + num2 a = 1
b = 2
c = 3
x = lazy(func, A, B)(a, b)
a = b
b = c
print a, b, c
x.func_b(a, b) #2 3 3
#
参考:http://blog.csdn.net/g9yuayon/article/details/759778
http://blog.donews.com/superisaac/archive/2006/03/16/771387.aspx
Lazy evaluation的更多相关文章
- 泛函编程(11)-延后计算-lazy evaluation
延后计算(lazy evaluation)是指将一个表达式的值计算向后拖延直到这个表达式真正被使用的时候.在讨论lazy-evaluation之前,先对泛函编程中比较特别的一个语言属性”计算时机“(s ...
- 学习笔记之Lazy evaluation
Lazy evaluation - Wikipedia https://en.wikipedia.org/wiki/Lazy_evaluation In programming language th ...
- lazy evaluation and deferring a computation await promise async
Promise - JavaScript | MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_ ...
- PLT:说说Evaluation strategy
Brief 在学习方法/函数时,我们总会接触到 按值传值 和 引用传值 两个概念.像C#是按值传值,但参数列表添加了ref/out后则是引用传值,但奇怪的事出现了 namespace Foo{ cla ...
- django 中的延迟加载技术,python中的lazy技术
---恢复内容开始--- 说起lazy_object,首先想到的是django orm中的query_set.fn.Stream这两个类. query_set只在需要数据库中的数据的时候才 产生db ...
- lazy ideas in programming
lazy形容词,懒惰的,毫无疑问是一个贬义词.但是,对于计算机领域,lazy却是非常重要的优化思想:把任务推迟到必须的时刻,好处是避免重复计算,甚至不计算.本文的目的是抛砖引玉,总结一些编程中的laz ...
- lazy ideas in programming(编程中的惰性思想)
lazy形容词,懒惰的,毫无疑问是一个贬义词.但是,对于计算机领域,lazy却是非常重要的优化思想:把任务推迟到必须的时刻,好处是避免重复计算,甚至不计算.本文的目的是抛砖引玉,总结一些编程中的laz ...
- Linq之旅:Linq入门详解(Linq to Objects)
示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...
- LINQ Group By操作
在上篇文章 .NET应用程序与数据库交互的若干问题 这篇文章中,讨论了一个计算热门商圈的问题,现在在这里扩展一下,假设我们需要从两张表中统计出热门商圈,这两张表内容如下: 上表是所有政区,商圈中的餐饮 ...
随机推荐
- SQL-LINQ-Lambda 语法对照
SQL LINQ Lambda SELECT *FROM Employees from e in Employees select e Employees .Select (e => e) ...
- win7系统64位安装oracle10g
win7系统64位安装oracle10g 下载地址: http://download.oracle.com/otn/nt/oracle10g/10204/10204_vista_w2k8_x64_pr ...
- ThinkPHP框架搭建及常见问题(Apache或MySQL无法启动)----简单的初体验
有一定基础的人勿进,这篇讲的只是零基础入门,都是我刚接触以及我所了解到的人刚开始有疑惑的地方,具体框架介绍会在后面的博客中介绍 这一篇只是为了一个简单的页面显示而介绍的方法,不涉及代码,开发环境,所以 ...
- CheckStyle插件
如今代码静态检查越来越重要,已经成为构建高质量软件的不可或缺的一个验证步骤.如果你使用的是java语言,那么CheckStyle则是一个利器. CheckStyle能够帮助程序员检查代码是否符合制定的 ...
- 利用html5中的localStorage获取网页被访问的次数
利用html5中的localStorage获取网页被访问的次数 <!DOCTYPE html> <html> <head> <meta charset=&qu ...
- iOS下Html页面中input获取焦点弹出键盘时挡住input解决方案
问题描述 iOS系统下,移动web页面,inpu获取焦点弹出系统虚拟键盘时,偶尔会出现挡住input的情况,尽管概率不大,但是十分影响用户体验. 问题重现 原始页面:页面中有header.main.f ...
- jqGrid实现虚拟滚动和合并单元格
如图: 关键点:1.scroll: 1, //虚拟滚动,解决大数据一次性加载慢的问题,同时解决分页 2.function merge(names)//自定义函数 $(function () { //查 ...
- Linux回归篇-基础恶补
前言: LINUX在之前仅仅了解一些皮毛知识,最近计划深入研究大数据,LINUX当然少不了,所以借此良机将LINUX也顺带添加进入学习的目录之中.为了知识的全面性,本文先恶补一下LINUX的基础,作 ...
- Go语言实现-观察者模式
前前言 这个类经过我的正式投入使用啊,发现不对劲,这样做可能会导致线程死锁 比如你dispatch一个event,然后在这个回调里把那个事件的侦听给remove掉了,那么就会导致线程死锁(这个问题找了 ...
- cf459A Pashmak and Garden
A. Pashmak and Garden time limit per test 1 second memory limit per test 256 megabytes input standar ...