標籤:
在Python中,函數也是一種變數類型,也就是說可以用變數去儲存一個函數。
def hello(): print("hello")print(type(hello))>>> <class ‘function‘>
#將函數對象賦值為變數func,此時func指向了hello指向的函數對象func = hellofunc() #調用函數>>> hello
通過上面可以看出,我們可以把一個函數賦值為一個變數,那麼此變數就指向該函數對象。當然,我們也是可以修改hello執行的函數對象的,如:
def say_hi(): print("Hi") hello = say_hihello()>>> Hi
把say_hi函數對象賦值為hello,那麼hello就指向了say_hi函數對象,不再指向原先的函數對象了,也就無法再調用原先的函數對象。
基於函數的上述特性,我們可以很容易的實現一個函數功能表,比如我們常見的http的服務隊請求的處理,會根據請求的方法分別處理:
def handle_http_get(): print(‘handle http get request‘)def handle_http_post(): print(‘handle http post request‘)def handle_http_put(): print(‘handle http put request‘)http_handler = {‘get‘: handle_http_get, ‘post‘: handle_http_post, ‘put‘: handle_http_put}#上面分別實現了處理http的get,post,put請求#當請求的方法為get時,我們可以這麼處理:method = ‘get‘http_handler[method]()>>> handle http get request
2.閉包Closure
既然函數是對象,那麼就可以在函數中返回一個函數對象:
def get_func(): def func(): print(‘inside func‘) return funcf = get_func() # 此時f指向了func函數對象f() #執行func函數對象>>> inside func
一個函數與它的環境變數結合在一起,就構成Closure。
def power_factory(n): def power(list_arg): for i in range(len(list_arg)): list_arg[i] = list_arg[i]**n return power#計算list中的每個數的平方list1 = [1, 2, 3]f1 = power_factory(2)f1(list1)print(list1)f1(list1)print(list1)#計算List中每個數的立方list1 = [1, 2, 3]f2 = power_factory(3)f2(list1)print(list1)>>> [1, 4, 9][1, 16, 81][1, 8, 27]
對於power函數對象而言,power_factory函數傳遞的參數n就是其環境變數,會儲存到函數對象的__closure__屬性中。這樣子每次調用f1函數,執行的都是2的平方操作,所以上面連續兩次調用f1,輸出的都是平方數。如果我們需要執行立方操作,只需要重新調用power_factory,產生一個環境變數n = 3的閉包。
一般來說,像c編程中的函數,是沒有狀態的,除非在c函數中使用static變數來儲存其狀態,而使用閉包,我們可以很方便的做到函數都有其自己獨立的環境變數。
3. 裝飾器
函數對象有一個__name__屬性,表示函數的名字:
print(max.__name__)>>> max
如我們前面已經實現了一個hello函數,現在我們想增強hello函數的功能,在執行hello函數前,加入列印調試資訊。在不改變hello函數源碼的前提下,可以通過閉包來完成。
def debug(func): def wrapper(*args, **kw): print(‘call %s‘ % func.__name__) return func(*args, **kw) return wrapperhello = debug(hello)hello()>>> call hellohello
在上述的使用過程中,需要先執行hello = debug(hello), 感覺代碼總是有點怪怪的,為此Python中引入@文法,簡化使用的過程。
@debugdef hello(): print(‘hello‘) hello()>>>call hellohello
把@debug放在hello的前面,就相當於執行了hello = debug(hello)
如果需要在debug中傳遞參數,則需要使用3層函數定義:
def debug(text): def decorator(func): def wrapper(*args, **kw): print(‘%s: call %s‘ % (text, func.__name__)) func(*args, **kw) return wrapper return decorator@debug(‘debug_info‘)def hello(): print("hello")hello()>>>debug_info: call hellohello
把@debug(‘debug_info‘)放在hello的前面,相當於執行了hello = debug(‘debug_info‘)(hello)
總算寫完了,困了,睡覺了。
Python入門(九) 函數-閉包