標籤:
Functions as objects and closures
python中的函數是first-class objects
,即它們可以作為參數傳遞到其他函數中,可以儲存到資料結構中,也可以作為函數的傳回值
一個簡單例子
# foo.pydef callf(func): return func()
>>> import foo>>> def hello():... return ‘hello‘... >>> f = foo.callf(hello) # function as an arg>>> f‘hello‘
對上面代碼做一點修改:
# foo.pyx = 42def callf(func): return func()
>>> import foo>>> x = 37>>> def hello():... return ‘hello. x is %d‘ % x...>>> foo.callf(hello)‘hello. x is 37‘
這裡我們調用foo.callf(),但是x的值卻不是foo.py中定義的變數值,而是調用函數前上文的值。因此我們應該關注一個函數與周邊變數的關係,也就有了closure閉包的概念
closure
: 當一個函數的語句和它們所執行的環境一起打包的時候,就成為了所謂的closure 我們可以查看一個函數的__globals__
屬性,如:
>>> hello.__globals__{‘__builtins__‘: <module ‘__builtin__‘ (built-in)>,‘helloworld‘: <function helloworld at 0x7bb30>,‘x‘: 37, ‘__name__‘: ‘__main__‘, ‘__doc__‘: None‘foo‘: <module ‘foo‘ from ‘foo.py‘>}
可以看到hello裡面已經綁定了x=37
當使用嵌套函數時,閉包會給inner function
捕獲執行需要的環境,如
import foodef bar(): x = 13 def helloworld(): return "Hello World. x is %d" % x foo.callf(helloworld) # returns ‘Hello World, x is 13‘
這裡內建函式helloworld與x=13已經組成一個閉包
閉包可以怎樣使用?我們看一個延時賦值的例子:
from urllib import urlopendef page(url): def get(): return urlopen(url).read() return get
>>> python = page("http://www.python.org")>>> python<function get at 0x95d5f0>>>> pydata = python() # fetch http://www.python.org>>> pydata.__closure__(<cell at 0x67f50: str object at 0x69230>,)>>> python._ _closure_ _[0].cell_contents‘http://www.python.org‘
pydata對象在賦值的時候已經綁定了參數url的值,可以留到以後進行調用
Decorators
decorator裝飾器的作用是在裡面封裝其他函數或者類,用@號來表示,eg:
@tracedef square(x): return x*x
等同於
def square(x): return x*xsquare = trace(square)
這個例子中定義了一個square()函數,但馬上將它作為參數傳遞到trace()中,並將返回的對象替代原來的square。裝飾器顧名思義就是給原來函數增加一些裝飾,比如我們這裡trace函數可以做一些額外的事情,最後返回的對象可以是一個包含square的函數,即對我們的square函數做了“裝飾”
再看一個平常使用的例子:
enable_tracing = Trueif enable_tracing: debug_log = open("debug.log","w")def trace(func): if enable_tracing: def callf(*args,**kwargs): debug_log.write("Calling %s: %s, %s\n" % (func._ _name_ _, args, kwargs)) r = func(*args,**kwargs) debug_log.write("%s returned %s\n" % (func._ _name, r)) return r return callf else: return func
這個代碼就可以追蹤上面square函數的調用,如果enable_tracing,則用函數callf將func封裝起來,在裡面增加記錄日誌功能,並且callf最後還是返回func函數,也就是說如果允許追蹤的時候我們就將原來的函數對象裝飾成一個有追蹤功能的callf函數對象返回,如果不允許追蹤,就簡單返回func本身,這是非常精妙的一種設計
python closures and decorators