1. 保存有状态的过程
在scheme中使用cons
、car
、cdr
构造抽象数据,但cons构造的抽象数据却是一个过程:
(define (cons x y)
(define (dispatch m)
(cond ((= m 0) x)
((= m 1) y)
(else (error "argument not 0 or 1 -- cons" m))))
dispatch)
(define (car z) (z 0))
(define (cdr z) (z 1))
>> (define z cons(1 2))
>> (car z)
1
>> (cdr z)
2
>> (define l cons(z 3))
>> (car (car l))
1
解释一下:
-
变量作用域: local > enclosing > global > build-in
cons
函数中,x
,y
未在函数内部(local),外部(global)定义,也不是解释器内置的变量(build-in),x, y
的作用域即为enclosing。 -
closure: 内部函数中对enclosing作用域的变量进行引用
在
cons
函数中,定义函数dispatch
,dispatch
引用了x
,y
,并且作为cons
的返回值,当cons
执行完成之后,虽然会对内部变量回收,但dispatch
作为返回值,保留了x
,y
的引用。car
以dispatch
的函数类型为参数时,内部调用(dispatch 0)
,并返回其执行结果,即为(cons x y)
中的x
,同理(cdr dispatch)
返回y
。
由以上可知,过程(函数)不仅仅只是一系列动作的集合,它还可以保存有自己的状态(变量)。当我们使用cons(1, 2)
时,实际上得到了一个保持有自己变量1、2的过程。
2. 闭包
闭包即一个函数,通过它可以引用由包含由这个函数的代码所定义的变量,一个简单的例子(来自sicp)(这里采用了消息传递的方法):
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(define (dispath m)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
(else (error "Unknown request -- MAKE-ACCOUNT"
m))))
dispatch)
>> (define acc (make-account 100))
>> ((acc 'withdraw) 50)
50
>> ((acc 'deposit) 60)
110
>> ((acc 'withdraw) 120)
"Insufficient funds"
所以,闭包就是一个函数被当作值返回时,相当于返回了一个通道,这个通道可以访问这个函数enclosing作用域中的变量,即函数所需要的数据结构保存了下来,数据结构中的值在外层函数执行时创建,外层函数执行完毕时理应销毁,但由于内部函数作为值返回出去,这些值得以保存下来。而且无法直接访问,必须通过返回的函数,这也就是私有性。
显然,闭包的形成很简单,在执行过程完毕后,返回函数,或者将函数得以保留下来,即形成闭包。
可以查看scip中对环境模型的解释来理解闭包
3. Python的闭包
Python支持闭包(但python依然区分语句和表达式,所以明确指定return
语句返回函数)。
def cons(x, y):
def dispatch(m):
if m == 0:
return x
elif m == 1:
return y
else:
print("Argument not 0 or 1 -- CONS",m)
return dispatch
def car(z):
return z(0)
def cdr(z):
return z(1)
还有一点,有人用以下例子说明Python不能正确实现闭包:
funcs = [(lambda n: n + i) for i in range(10)]
print(funcs[3](5))
输出14,但这是因为存放的是i的引用,而非i的值。i的最终值为9,则funcs中所有的lambda引用i的值都为9。
这只是Python传引用的坑而已,并不能说明Python不能正确实现闭包,因为每个函数是可以存储变量的,比如:
funcs = [(lambda n, i=i: n + i) for i in range(10)]
print(funcs[3](5))
这次输出为8,因为使用局部变量拷贝每个外部变量i后,lambda可以存放不同的局部变量i。
4. Python装饰器
- 装饰器用来装饰函数
- 返回一个函数对象
- 被装饰函数标识符指向返回的函数对象
- 语法糖 @deco
import functools
def log1(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
def log2(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
import time
from functools import wraps
def logger(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
ts = time.time()
result = fn(*args, **kwargs)
te = time.time()
print("function = {0}".format(fn.__name__))
print("arguments = {0}{1}".format(args, kwargs))
print("return = {0}".format(result))
print("time = %.6f sec" % (te - ts))
return result
return wrapper
@logger
def multipy(x, y): # multipy = logger(multipy)
return x*y
@logger
def sum_num(n): # sum_num = logger(sum_num)
s = 0
for i in range(n+1):
s +=i
return s
print(multipy(2, 10))
print(sum_num(100))
from functools import wraps
def memo(fn):
cache = {}
miss = object()
@wraps(fn)
def wrapper(*args):
result = cache.get(args, miss)
if result is miss:
result = fn(*args)
cache[args] = result
return result
return wrapper
@memo
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
javascript
var fibonacci = function() {
var a = 1;
var b = 1;
return function() {
var temp = b;
b = a+b;
a = temp;
return b;
}
}
var fibInstance = fibonacci();
- older
- Newer