# Python函数式编程系列007：惰性求值

## 缘起

``````def f(x): # x储存了某种我们需要的状态
## 所有可以提前计算的放在这里
z = x ** 2 + x + 1
print('z is {}'.format(z))
def helper(y):
## 所有延迟计算的放在这里
return y * z
return helper``````

``````>>> g = f(1)
z is 3
>>> g(2) + g(1) # 可以看到这次就不会打印`z is xxxx`的输出了
9``````

## 惰性属性与惰性值

1. 周长(`perimeter`)
2. 面积(`area`)
3. 圆最上面坐标的位置(`upper_point`)
4. 圆心到原点的距离(`distance_from_origin`)
5. ...

``````@dataclass
class CircleInitial:
x: float
y: float
r: float

def __init__(self, x, y, r):
self.x = x
self.y = y
self.r = r

self.perimeter = 2 * r
self.area = r * r * 3.14
self.upper_point = (x, y + r)
self.lower_point = (x, y - r)
self.left_point = (x - r, y)
self.right_point = (x + r, y)
self.distance_from_origin = (x ** 2 + y ** 2) ** (1/2)``````

``````@dataclass
class CircleMethod:
x: float
y: float
r: float

def area(self):
print("area calculating...")
return self.r * self.r * 3.14``````

``````@dataclass
class CircleMethod:
x: float
y: float
r: float

@property
def area(self):
print("area calculating...")
return self.r * self.r * 3.14``````

``````>>> a = CircleMethod(1, 2, 3)
>>> a.area ** 2 + a.area + 1
area calculating...
area calculating...
827.8876000000001``````

1. 这个属性不会初始化的时候计算
2. 这个属性只在被调用时计算
3. 这个属性只会计算一次，后面不会调用

``````def lazy_property(func):
attr_name = "_lazy_" + func.__name__

@property
def _lazy_property(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, func(self))
return getattr(self, attr_name)

return _lazy_property``````

``````@dataclass
class Circle:
x: float
y: float
r: float

@lazy_property
def area(self):
print("area calculating...")
return self.r * self.r * 3.14``````

``````>>> b = Circle(1, 2, 3)
>>> b.area ** 2 + b.area + 1
area calculating...
827.8876000000001``````

``````class _LazyValue:

def __setattr__(self, name, value):
if not callable(value) or value.__code__.co_argcount > 0:
raise NotVoidFunctionError("value is not a void function")
super(_LazyValue, self).__setattr__(name, (value, False))

def __getattribute__(self, name: str):
try:
_func, _have_called = super(_LazyValue, self).__getattribute__(name)
if _have_called:
return _func
else:
res = _func()
super(_LazyValue, self).__setattr__(name, (res, True))
return res
except:
raise AttributeError(
"type object 'Lazy' has no attribute '{}'"
.format(name)
)

lazy_val = _LazyValue()``````

``````def f():
print("f compute")
return 12

>>> lazy_val.a = f
>>> lazy_val.a
f compute
12
>>> lazy_val.a
12``````

## 惰性迭代器/生成器

``````>>> a = (i for i in range(5))
>>> list(a)
[0, 1, 2, 3, 4]
>>> list(a)
[]``````

``````def f(x):
print("f")
return x + 1

def g(x):
print("g")
return x + 1``````

``````>>> a = (g(i) for i in (f(i) for i in range(5)))
>>> next(a)``````

``````>>> temp = [f(i) for i in range(5)]
>>> res = g(temp[0])``````

``>>> res = (g(f(i)) for i in range(5))``

``````from itertools import repeat

repeat_1 = repeat(1)``````

``````res = (g(i) for i in (i * 3 for i in repeat_1))
next(res)``````

0 条评论