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应用程序与数据库交互的若干问题 这篇文章中,讨论了一个计算热门商圈的问题,现在在这里扩展一下,假设我们需要从两张表中统计出热门商圈,这两张表内容如下: 上表是所有政区,商圈中的餐饮 ...
随机推荐
- google visit
http://emuch.net/bbs/viewthread.php?tid=7630684&fpage=3&target=blank 内Facebook,twitter,dropb ...
- win7使用右键导致死机、假死、explorer无法响应的解决方法
右键引起explorer无法响应,奔溃,主要是由于COMCTL32.DLL和COMCTL21.OCX文件引起的 描述:comctl32.dll是Windows应用程序公用GUI图形用户界面模块.报告提 ...
- 区段extent及数据块
一.区段是表空间中由某个段所使用的一块磁盘空间.他是一组连续的oracle数据块.引入extent的目的是为了减少磁盘空间分配的次数,如果是采用oracle数据块直接分配的话就增加了oracle磁盘空 ...
- HTML5学习笔记-video
HTML5 <video> - 方法.属性以及事件 方法 play()——播放 pause()——暂停 load()——加载 canPlayType——检测您的浏览器是否能播放不同类型的视 ...
- 匈牙利算法(素数伴侣(HW1112))
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<vector> #include<string&g ...
- Substrings(hd1238)
Substrings Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total ...
- NFC扫描
目录(?)[-] 简介 什么是NFC 标签和读取器 标签 读取器 NFC模式 通信模式 主动模式 被动模式 操作模式 读写 点对点 模拟卡片 NFC用例 用于启动服务 用于分享 用于连接设备 用于买票 ...
- JVM调优的几种策略(转)
JVM参数调优是一个很头痛的问题,可能和应用有关系,别人说可以的对自己不一定管用.下面是本人一些JVM调优的实践经验,希望对读者能有帮助,环境LinuxAS4,resin2.1.17,JDK6.0,2 ...
- perl /m 当作多行处理
高级用法 多行匹配: zjtest7-frontend:/root/0825# cat a2.pl print "1111111111111\n"; my $_="abc ...
- cf459D Pashmak and Parmida's problem
D. Pashmak and Parmida's problem time limit per test 3 seconds memory limit per test 256 megabytes i ...