对python新手而言,要理解闭包、装饰器,就要先懂下面这些东东!
先来看下一个函数接受另外一个函数以及一个数字当作参数,并且重复调用指定函数指定次数
cat test.py def hello(): # hello函数 print("hello") def repeat(fn,times): # 函数名是函数的入口地址 for i in range(times): fn() repeat(hello,3) python test.py # 执行结果 hello hello hello
函数也可以在其它函数内部声明
cat test.py def print_integer(values): def is_integer(value): try: return value == int(value) except: return False for v in values: if is_integer(v): print(v) python test.py # 执行结果 1 2 3
# 相比函数当作参数传递,我们可以将它包装在另外的函数中,从而向函数增加新的行为。
# 向函数增加跟踪输出,有助于我们理解 def hello(): print("hello") def repeat(fn,times): for i in range(times): fn() def print_call(fn): def fn_wrap(*args, **kwargs): #take any arguments print ("Calling %s" % (fn.func_name)) return fn(*args, **kwargs) #pass any arguments to fn() return fn_wrap test = print_call(hello) print "Returning func name: %s" % test.func_name repeat(test,2) python test.py # 执行结果 Returning func name: fn_wrap # 函数改变了 Calling hello hello Calling hello hello # 如果想包装一个函数同时保留它原来的函数名,可以这样实现 def print_call(fn): def fn_wrap(*args, **kwargs): #take any arguments print ("Calling %s" % (fn.func_name)) return fn(*args, **kwargs) #pass any arguments to fn() fn_wrap.func_name = fn.func_name # 原来的函数名赋值给返回的函数名 return fn_wrap python test.py # 执行结果 Returning func name: hello # 函数名保持一致 Calling hello hello Calling hello hello # 下面该讲闭包了,闭包的定义有很多,简单点说就是:一个可以引用在函数闭合范围内变量的函数 a = 3 def get_a(): return a print get_a() python test.py # 执行结果 3 a = 3 def get_a(): return a def set_a(val): a = val set_a(5) print get_a() python test.py # 执行结果还是3 3 # 这里闭包不能写入任何被捕获的变量,a = val实际上写入了本地变量a,隐藏了全局的a。为了解决这种限制,可以用一个容器类型 class A(object): pass a = A() a.value = 3 def get_a(): return a.value def set_a(val): global a a.value = val set_a(5) print get_a() python test.py # 执行结果 5 # 我们知道函数从它的闭合范围捕获变量,现在我们可以学习偏函数(prtial)了 # 一个偏函数是一个填充了部分或全部参数的函数的实例,很多时候偏函数可以消除代码的重复 # 偏函数默认pythhn库已经封装好了,只需要from functools import partial即可 def get_stuff(user,pw,stuff_id): print("user: %s,pw: %s,stuff_id: %s" % (user,pw,stuff_id)) def partial(fn,*args,**kwargs): def fn_part(*fn_args,**fn_kwargs): # *可以传参tuple, **可以传参dict kwargs.update(fn_kwargs) return fn(*args + fn_args, **kwargs) return fn_part my_stuff = partial(get_stuff,'user','pass') my_stuff(5) python test2.py # 执行结果 user: user,pw: pass,stuff_id: 5 # 最后我们来看看函数装饰器,函数装饰器接受一个函数作为参数然后返回一个新的函数。 # 装饰器本质是:改变了函数的代码入口点 # 装饰器就是一个函数,这个函数可以是内置的(@staticmethod、@classmethod),也可以是自定义的函数。 def print_call(fn): def fn_wrap(*args, **kwargs): #take any arguments print ("Calling %s" % (fn.func_name)) return fn(*args, **kwargs) #pass any arguments to fn() fn_wrap.func_name = fn.func_name return fn_wrap @print_call # 使用@符号标记,挺方便的! def will_be_logged(arg): return arg*5 print will_be_logged('#') python test.py # 执行结果 Calling will_be_logged ##### # 内置修饰符 # @staticmethod vs @classmethod # @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样,只能直接类名.属性名或类名.方法名。 # @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数,有cls参数,可以来调用类的属性,类的方法,实例化对象。 # @property 对类属性的操作,类似于java中定义getter/setter class A(object): bar = 1 def foo(self): print 'foo' @staticmethod def static_foo(): print 'static_foo' print A.bar @classmethod def class_foo(cls): print 'class_foo' print cls.bar cls().foo() A.static_foo() A.class_foo() 输出结果: static_foo 1 class_foo 1 foo # @property 对类属性的操作,类似于java中定义getter/setter class B(): def __init__(self): self.__prop = 1 @property def prop(self): print "call get" return self.__prop @prop.setter def prop(self, value): print "call set" self.__prop = value @prop.deleter def prop(self): print "call del" del self.__prop # @引用多个函数装饰器 def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrappeddef def makeitalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped @makebold @makeitalic def hello(): return "hello world" print hello() # 执行结果如下 <b><i>hello world</i></b> # 给装饰器传参 def require(role): def wrapper(fn): def new_fn(*args, **kwargs): if not role in kwargs.get('roles', []): print("%s not in %s" % (role, kwargs.get('roles', []))) raise Exception("Unauthorized") return fn(*args, **kwargs) return new_fn return wrapper @require('admin') def get_users(**kwargs): return ('Alice', 'Bob') print get_users(roles=['user','admin']) python test.py # 执行结果 ('Alice', 'Bob') # 函数装饰器就是这样了!未来可能有类装饰器。
扩展
decorator模块
decorator模块是 Michele Simionato 为简化python的decorator的使用难度而开发的, 使用它,你可以更加容易的使用decorator机制写出可读性、可维护性更好的代码
参考链接
http://book42qu.readthedocs.org/en/latest/python/python-closures-and-decorators.html
http://blog.csdn.net/terry_tusiki/article/details/14223649
http://blog.csdn.net/handsomekang/article/details/9615239
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386820062641f3bcc60a4b164f8d91df476445697b9e000(@property sample)