函数闭包与装饰器

闭包

闭包是由函数与其相关的引用环境组合而成的实体。即如果在一个内部函数里,对在外部作用域(但不是全局作用域)的变量进行引用,那么内部函数就是被认为是闭包,这个变量与函数的定义处于同一级别。
在Python中,函数是作为对象存在的,可以作为某个函数的返回结果,因此支持闭包。
与C语言中的函数指针相比,闭包允许嵌套函数访问其作用域外的non-local变量,这与Python解释器对变量的作用域查找规则有关。
对于运行时内存分配模型会在线性栈上创建局部变量的语言来说(典型如C语言),通常很难支持闭包。因为这些语言底层实现中,若函数返回,则函数中定义的局部变量均会随着函数栈被回收而销毁。但闭包在底层实现上要求其要访问的non-local变量在闭包被执行的时候保持有效,直到这个闭包的生命周期结束,这意外着这些non-local变量只有在其确定不再被使用时才能销毁,而不能随着定义这些变量的函数返回销毁。因此,天生支持闭包的语言通常采用garbage collection的方式管理内存,因为gc机制保证了变量只有不再被引用时才会由系统销毁并回收其内存空间。

简单的示例

def addx(x):
	def adder(y):
		return x+y
	return adder

if __name__ == "__main__":
	addone = addx(1)
	addtwo = addx(2)
	print "1+1=",addone(1)
	print "1+2=",addtwo(1)

说明:如果在一个内部函数里(adder)中对外部变量(x,x在外部作用域addx)进行引用,则返回的adder就是一个闭包。即函数闭包=函数块+定义函数时的环境,adder是函数块,x就是环境。

优点

  1. 当闭包执行完后,仍然能够保持住当前的运行环境。
  2. 能够针对不同环境(利用外部函数的参数),生成具有不同功能的函数对象,即闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。
    3 .提高代码可复用性

闭包使用的注意事项

  1. 闭包不能修改外部作用域的局部变量,如果在内部函数中定义了一个相同名字的局部变量,那么会隐藏外部的局部变量
  2. 经典的内部函数错误
def outer():
	x = 1
	def inner():
		x = x+1
		return x
	return inner

if __name__ == "__main__":
	c = outer()
	print c()
# Traceback (most recent call last):
#   File "test.py", line 10, in <module>
#     print c()
#   File "test.py", line 4, in inner
#     x = x+1
# UnboundLocalError: local variable 'x' referenced before assignment

首先x = xxx这种形式,Python解析器认为要在内部函数bar内创建一个新的局部变量x,同时外部函数outer里的x在inner里已不可见,而解析器对接下来对右边的x + 1的解析就是用本地的变量x加1,而这时左边的a即本地的变量x还没有创建(等右边赋值呢),x未定义,就会报错
解决方法:

  1. 将x设置为容器,如list,通过x[0]=x[0]+1来实现,这里的x[0]不会被看作是一个局部变量
  2. 用别名替代比如y=x+1,内部函数只能引用外部函数的变量
    3.python3使用nonloacal

装饰器

“装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有认证,权限检查,记日志,检查参数,加锁,等等等等,这些功能和系统业务无关,但又是系统所必须的。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。”
装饰器是基于闭包函数实现的

无参数的装饰器
def decorator(func):
	def myfunc(para):
		print "begin"
		func(para)
		print "end"
	return myfunc
@decorator
def myfunc(name):
	print "my name is ",name

myfunc("kanglei")

# begin
# my name is  kanglei
# end
#上述装饰器相当于调用:myfunc = decorator(myfunc)(name),相当于外部的myfunc已经指向decorator函数内部的myfunc

装饰器可以用def的形式定义,如上面代码中的decorator。装饰器接收一个可调用对象作为输入参数,并返回一个新的可调用对象。装饰器新建了一个可调用对象,也就是上面的myfunc。myfunc中,我们增加了打印一些参数的功能。
内部函数的参数必须和要被修饰的函数的参数保持一致

含参数的装饰器
def log(text):
	def decorator(func):
		def myfunc(para):
			print text
			func(para)
		return myfunc
	return decorator

@log("^_^")
def myfunc(name):
	print "my name is ",name

myfunc("kanglei")

# ^_^
# my name is  kanglei
#上述装饰器相当于调用:myfunc = log("begin")(myfunc)(name)
装饰器还可用于修饰类,例如实现单例模式
def singleton(cls):
	instances = {}
	def get_instance():
		if cls not in instances:
      	instances[cls] = cls()
    	return instances[cls]
  	return get_instance
 
@singleton
class MyClass:
    pass

当使用装饰器时,函数对象已经发生改变,如上面的外部的myfunc已经变为内部函数myfunc,其__name__会发生改变(如果函数名不同),这对于依靠函数签名__name__的程序会发生错误,因此需要某种方法来改变其__name__。
1.使用wrapper.__name__ = func.__name__
2.使用Python内置的functools.wraps装饰器


import functools

def log(text):
	def decorator(func):
		@functools.wraps(func)
		def wrapper(para):
			print text
			func(para)
		return wrapper
	return decorator

@log("^_^")
def myfunc(name):
	print "my name is ",name

print myfunc.__name__

# myfunc

小结

在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
装饰器可以让函数轻装上阵,更重要的是将函数的约束放置于接口处,使意图更加明了,同时又不增加调用者的负担。
多个参数器可组合使用

支持不定参数数目的装饰器,使用*args, **kwargs

import functools
 
def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print text
            func(*args, **kwargs)
        return wrapper
    return decorator
 
@log("^_^")
def myfunc(name):
    print "my name is ",name
 
@log("^_^")
def myfunc2(name,age):
    print "name:",name," age:",age
 
myfunc("kanglei")
myfunc2("kanglei",23)
 
# ^_^
# my name is  kanglei
# ^_^
# name: kanglei  age: 23

参考

  1. http://blog.csdn.net/thy38/article/details/4471421
  2. http://blog.csdn.net/marty_fu/article/details/7679297
  3. http://www.ibm.com/developerworks/cn/linux/l-cn-closure/
  4. http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386819879946007bbf6ad052463ab18034f0254bf355000
posted @ 2015-03-20 21:25  zhainankl  阅读(177)  评论(0编辑  收藏  举报