b = 1
c = 2
a = b
b = c
那么a 是多少? 当然是1了,一般的程序语言执行,都得到这样的结果。但是通过 lazy evaluation,你将得到不同的结果: a = 2, 因为a依赖于b,而b依赖于c。 我用python语言实现了一个lazy evaluation的上下文,实例如下.
le = LazyContext()
# 使用decorator lazilize, 函数也可以Lazy化.
@le.lazilize
def myadd(a, b):
print "now calculate"
return a + b
le.b = 1
le.c = 2
le.a = le.b * 8
print "a is", le.evaluate(le.a)
le.d = myadd(9, 12)
le.f = le.b
le.b = le.c
print "a is", le.evaluate(le.a)
print "d is ", le.evaluate(le.d)
print "f is ", le.evaluate(le.f )
得到的结果是
a is 8
a is 16
d is now calculate
21
f is 2
支持多种运算符号 +, -, *, /, neg, and , or not, ….. 但是限制是,一个二元运算符号,必须lazy变量放在前面。 如 le.a + 8 是合法的,8 + le.a就不合法了. 如果仔细照料,应该不是一个大问题。后附代码
#lazy.py
import operator
supported_ops = ['__abs__', '__add__', '__and__', '__concat__',
'__contains__', '__delitem__', '__delslice__',
'__div__', '__eq__', '__floordiv__', '__ge__', '__getitem__',
'__getslice__', '__gt__', '__inv__', '__invert__',
'__le__', '__lshift__', '__lt__', '__mod__', '__mul__',
'__ne__', '__not__', '__or__',
'__pos__', '__pow__', '__repeat__', '__rshift__',
'__setitem__', '__sub__', '__setslice__', ]
def call_object(name):
return lambda self, *args: Lazy_M(self.context, name, self, *args)
def set_ops(cls):
global suported_ops
for fname in supported_ops:
setattr(cls, fname, call_object(fname))
class LazyObj:
name = ""
class NameRef(LazyObj):
def __init__(self, ref_name):
self.ref_name = ref_name
class Lazy_SimpleObj(LazyObj):
def __init__(self, context, value):
self.value = value
self.context = context
def evaluate(self):
return self.context.evaluate(self.value)
class Lazy_M(LazyObj):
def __init__(self, context, funcname, opj, *args, **kwargs):
self.args, self.kwargs = args, kwargs
self.opj = opj
self.funcname = funcname
self.context = context
def evaluate(self):
tv = self.context.evaluate(self.opj)
f = getattr(tv, self.funcname)
if callable(f):
a, b = [self.context.evaluate(t) for t in self.args], dict([(k, self.context.evaluate(v)) for k, v in self.kwargs.items()])
return f(*a, **b)
class Lazy_F(LazyObj):
def __init__(self, context, func, *args, **kwargs):
self.args, self.kwargs = args, kwargs
self.func = func
self.context = context
def evaluate(self):
return self.func( *[self.context.evaluate(t) for t in self.args], **dict([(k, self.context.evaluate(v)) for k, v in self.kwargs.items()]))
class LazyContext:
def __init__(self):
self.__lazy_objs = {}
set_ops(LazyObj)
def lazy_objs(self):
return self.__lazy_objs
def evaluate(self, obj):
if obj.__class__ == NameRef:
return self.evaluate(self.__lazy_objs[obj.ref_name])
elif isinstance(obj, LazyObj):
return obj.evaluate()
else:
return obj
def __setattr__(self, name, value):
if name.find("__") < 0:
if isinstance(value, LazyObj):
if value.name: value = NameRef(self, value.name)
else:
value = Lazy_SimpleObj(self, value)
value.context = self
value.name = name
self.__lazy_objs[name] = value
else:
self.__dict__[name] = value
def __getattr__(self, name):
if name in self.__lazy_objs:
obj = self.__lazy_objs[name]
nr = NameRef(self, name)
return nr
else:
return self.__dict__[name]
def lazilize(self,func):
def __lazy_call(*args, **kwargs):
m = Lazy_F(self, func, *args, **kwargs)
return m
return __lazy_call