標籤:
在python中,函數名也是一個變數,代表對一個函數內容的引用,意味著可以作為參數傳入到其他函數中,根據這個特性,發散出裝飾器、閉包等概念,並涉及到變數範圍等問題。
函數
python中函數操作符為(),在任何需要調用函數的地方都需要在函數名後面加(),表示調用該函數,否則的話僅僅表示一個函數對象,當然可以儲存這個對象,或者將該函數對象傳入到其他函數中,以供延遲調用。
函數的參數分為按順序確定的位置參數、預設參數、非關鍵字可變長度參數、關鍵字可變長度參數。
其中位置參數僅僅通過傳入時的順序來確定調用,並且如果沒有任何預設參數的話,傳入的參數數目必須和函數定義一致。python支援預設參數,將一些常用的參數初始化而避免重複寫相同的參數。這兩個都是常規函數的參數形式。另外一種非常規的參數就是參數的數目不一定,即可變長度的參數,這些參數可以是不帶關鍵字的元組,或者是帶關鍵字的字典。
python中,參數傳入規則可以表示如下:
def myFunc([formal_args,] *args_tuple,**kw_dict)
其中formal_args表示位置參數,包含可能有的預設參數,而*args_tuple則表示可變長度的非關鍵字參數,**kw_dict表示可變長度的關鍵字參數。
匿名函數 lambada
匿名函數是python中對單行語句的特殊用法,用於簡化一些簡單函數。如
fun add(x,y):
? ? return x+y
可以用lambada x,y:x+y來表示,lambada之後,冒號之前的內容表示需要傳入的參數,冒號之後的內容表示執行的單行語句。
lambada可以被儲存,以便之後調用,如 add = lambada x,y:x+y,可以調用為add(x,y)。
裝飾器 decorator
由於函數的本質是一個引用,所以對函數名可以進行傳入、傳出等和一般變數一樣的操作,通過這個特性,可以設計一些裝飾器,用以對函數做重複的通用操作。python中對裝飾器有額外的文法支援:@,在函數的上方加入@decorator語句,即表示對該函數進行裝飾調用。一個函數可以被多個裝飾器裝飾。裝飾器的本質是函數,所以裝飾器也支援參數傳入。一個裝飾器的例子:
def log(func):
? ? def wrappedFunc():
? ? ? ? print ’The %s() is called!’ % func.__name__
? ? ? ? return fun()
? ? return wrappedFunc
通過@log的方式使用裝飾器:
@log
def func():
? ? pass
上述語句的含義是func=log(func),此時,func的內容會調用裝飾器log,log函數最終會返回wrappedFunc函數對象,但是並沒有調用該函數,當func()執行時真正調用wrappedFunc(),該函數首先列印一行調用日誌,然後返回func()函數的調用,這裡返回的調用,並不是func函數對象,所以在這裡會真正執行func函數的實體。從而實現“裝飾”的過程。
閉包 closure
在python2.1之後,範圍規則變為靜態範圍,內建函式可以正常引用外部的變數,這種內建函式引用外部範圍的變數的函數就被稱為閉包:
def counter(start_at = 0):
? ? count = start_at
? ? def incr():
? ? ? ? count += 1
? ? ? ? return count[0]
? ? return incr
可以看到閉包的形式裝飾器很像,區別是閉包傳入的不一定是一個函數,只是對外部變數的引用,內建函式中也不一定需要執行傳入的函數,可能做一些其他的工作。
閉包和裝飾器的一個例子:
from time import time
def logged(when):
? ? def log(f,*args,**kargs):
? ? ? ? print ‘’’Called:
function: %s
args: %r
kargs: %r’’’ %(f,args,kargs)
? ? def pre_logged(f):
? ? ? ? ?def wrapped(*args,**kargs):
? ? ? ? ? ? log(f,*args,**kargs)
? ? ? ? ? ? return f(*args,**kargs)
? ? ? ? return wrapped
? ? def ?post_logged(f):
? ? ? ? def wrapped(*args,**args):
? ? ? ? ? ? ?now = time()
? ? ? ? ? ? ?try:
? ? ? ? ? ? ? ? return f(*args,**kargs)
? ? ? ? ? ? finally:
? ? ? ? ? ? ? ? log(f,*args,**kargs)
? ? ? ? ? ? ? ? print “time delta: %s “ % (time()-now)
? ? ? ? return wrapped
? ? try:
? ? ? ? return {‘pre’: pre_logged,’post’:post_logged}[when]
? ? except KeyError,e:
? ? ? ? raise ValueError(e),’must be “pre” or “post” ‘
@logged(“post")
def hello(name):
? ? print “Hello,”,name
hello(“word!")?
偏函數
通過引用functools 模組中的partial 可以引入偏函數,偏函數的概念就是對原本需要多個參數的函數確定其中一個參數而稱為另一個函數:
from operator import add
from functools import partial
add1 = partial(add,1)
此時,add1(5) 就表示add(1,5)
範圍和globa語句
python搜尋一個標示符的時候,會先從局部變數開始搜尋,如果沒有找到,則從上一級搜尋,直到沒有找到而返回NameError
通過globa關鍵字,可以將變數推出局部範圍,不通過上述尋找過程,而直接引用一個已命名的全域變數。
產生器和yield語句
產生器用yield語句產生迭代器,每個yield語句順序地使其內容在next()方法中被調用:
def simpleGen():
? ? yield 1
? ? yield 2
此時,simpleGen()就是一個迭代器,simpleGen().next() = 1,simpleGen().next() = 2
除了next()方法獲得下一個值,還可以通過send方法來講值送給產生器:
def counter(start_at = 0):
? ? count = start_at
? ? while True:
? ? ? ? val = (yield count)
? ? ? ? if val is not None:
? ? ? ? ? ? count = val
? ? ? ? else :
? ? ? ? ? ? ?count += 1
count = count(5)
count.next() = 5;count.next() = 6;count.send(9) = 9; count.next() = 10; count.close() ; ?count.next(): StopIteration?
Python學習_08_函數式編程