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应用程序与数据库交互的若干问题 这篇文章中,讨论了一个计算热门商圈的问题,现在在这里扩展一下,假设我们需要从两张表中统计出热门商圈,这两张表内容如下: 上表是所有政区,商圈中的餐饮 ...
随机推荐
- linux重要的标准目录和文件
linux重要的标准目录和文件 / 根目录,所有其他文件在根文件系统的子目录下 /bin 基本命令的二进制文件,存放linux下常用的命令和工具 /boot 引导加载器的固有文件,linux就是从这里 ...
- iOS开发那些事儿(一)轮播器
前言 市面上绝大部分的APP被打开之后映入眼帘的都是一个美轮美奂的轮播器,所以能做出一个符合需求.高效的轮播器成为了一个程序员的必备技能.所以今天的这篇博客就来谈谈轮播器这个看似简单的控件其中蕴含的道 ...
- ‘Microsoft.Jet.OLEDB.4.0’ provider is not registered
正如以下msdn论坛所述,Microsoft.Jet.OLEDB.4.0只有32bit,所以我们必须compile application to x86 platform. http://social ...
- 【Lucene4.8教程之一】使用Lucene4.8进行索引及搜索的基本操作
在Lucene对文本进行处理的过程中,可以大致分为三大部分: 1.索引文件:提取文档内容并分析,生成索引 2.搜索内容:搜索索引内容,根据搜索关键字得出搜索结果 3.分析内容:对搜索词汇进行分析,生成 ...
- Fragment 之 PagerAdapter
package com.edaixi.main.adapter; import android.content.Context; import android.support.v4.view.Page ...
- C语言--基本运算符
一.算术运算符 1. 加法运算符 + * 除了可以进行加法运算外,还可以表示正号:+521 2.减法运算符 — * 除了可以进行减法运算外,还可以表示负号:—741 3.乘法运算法符 * * 请注意符 ...
- MYSQL 数据类型的 3 个注意
注意 1. bit(Length) 这种数据类型中,最大长度只可以是64.就是说 bit(2) 对 bit(64) 对 bit(65) 错 bit(100) 错 注 ...
- HeadFirst设计模式读书笔记(4)-工厂模式
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个.工厂方法让类把实例化推迟到子类. 所有工厂模式都用来封装对象的创建.工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象 ...
- SSD和HDD的区别
SSD与HDD最大的不同是:它没有马达.盘片.磁头摇臂这些HDD必需的机械部件,这是由两种硬盘不同的工作原理所决定的.SSD相比HDD来说节省了机械部件运动的时间,并且SSD所使用的主要存储元件NAN ...
- QT:用QSet储存自定义结构体的问题——QSet和STL的set是有本质区别的,QSet是基于哈希算法的,要求提供自定义==和qHash函数
前几天要用QSet作为储存一个自定义的结构体(就像下面这个程序一样),结果死活不成功... 后来还跑到论坛上问人了,丢脸丢大了... 事先说明:以下这个例子是错误的 #include <QtCo ...