對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)