函數作用:把一些複雜的代碼封裝起來,函數一般都是一個功能,用的時候才調用,提高重複使用率和簡化程式結構。
<b>5.1 文法</b>
def functionname(parms1, parms2, ...):
code block
return expression
函數以def關鍵字開頭,空格後跟函數名,括号裡面是參數,用于傳參,函數代碼段裡面引用。
<b>5.2 函數定義與調用</b>
# 定義函數
>>> def func():
... print "hello world!"
... return "hello world!"
...
# 調用函數
>>> func()
hello world!
'hello world!'
當我們定義好函數,是不執行的,沒有任何輸出。當輸入函數名後跟雙小括号才會執行函數裡寫的代碼。
<b>順便說下print和return差別:</b>
有沒有點奇怪!為什麼print和return輸出一樣呢,return就加個單引号,貌似也沒啥明顯差別啊!其實在解釋器下所有的結果都會輸出的。
先了解下return作用:結束函數,并傳回一個值。如果不跟表達式,會傳回一個none。
好,那麼我們深入了解下他們差別,舉個例子,寫個py程式:
#!/usr/bin/env python
def func():
print "1: hello world!"
return "2: hello world!"
func()
# python test.py
1: hello world!
明白點了嘛?print是列印對象的值,而return是傳回對象的值。也就是說你return預設是将對象值存儲起來,要想知道裡面的值,可以用print可以列印。
print func()
2: hello world!
為什麼函數裡面不用print就在這裡,往往我們定義一個函數是不需要列印的,而是交給其他代碼去處理這個函數傳回值。當然,print在調試函數代碼時會起到很好的幫助。
<b>5.3 函數參數</b>
<b>5.3.1 接受參數</b>
>>> def func(a, b):
... print a + b
>>> func(1, 2)
3
>>> func(1, 2, 3)
traceback (most recent call last):
file "<stdin>", line 1, in <module>
typeerror: func() takes exactly 2 arguments (3 given)
a和b可以了解為是個變量,可由裡面代碼塊引用。調用函數時,小括号裡面的表達式數量要對應函數參數數量,并且按傳參按位置賦予函數參數位置。如果數量不對應,會抛出typeerror錯誤。
當然,函數參數也可以是數組:
>>> def func(a):
... print a
>>> func([1,2,3])
[1, 2, 3]
>>> func({'a':1,'b':2})
{'a': 1, 'b': 2}
如果不想一一對應傳參,可以指定參數值:
>>> def func(a,b):
>>> func(b=2,a=1)
<b>5.3.2 函數參數預設值</b>
參數預設值是預先定義好,如果調用函數時傳入了這個值,那麼将以傳入的為實際值,否則是預設值。
>>> def func(a, b=2):
>>> func(1)
>>> func(1, 3)
4
<b>5.3.3 接受任意數量參數</b>
上面方式固定了參數多個,當不知道多少參數時候可以用以下方式。
單個星号使用:
>>> def func(*a):
>>> func(1,2,3)
(1, 2, 3)
單個星号存儲為一個元組。
兩個星号使用:
>>> def func(**a):
>>> func(a=1, b=2, c=3)
{'a': 1, 'c': 3, 'b': 2}
兩個星号存儲為一個字典。可見它們都是以數組的形式傳入。
你也許在查資料的時候,會看到這樣寫的函數參數(*args, **kwargs),與上面隻是名字不一樣罷了 :
>>> def func(*args, **kwargs):
... print args
... print kwargs
>>> func(1,2,3,a=1,b=2,c=3)
與普通參數一起使用:
>>> def func(a, b, *c):
... print c
>>> func(1,2,3,5,6)
(3, 5, 6)
>>> def func(a, b, **c):
>>> func(1,2,a=1,b=2,c=3)
typeerror: func() got multiple values for keyword argument 'a'
>>> func(1,2,c=3,d=4,e=5)
{'c': 3, 'e': 5, 'd': 4}
抛出異常,是因為傳入的第一個參數1,和第三個參數a=1,都認為是傳入函數參數a了。請注意下這點。
<b>5.4 作用域</b>
作用域聽着挺新鮮,其實很簡單,就是限制一個變量或一段代碼可用範圍,不在這個範圍就不可用。提高了程式邏輯的局部性,減少名字沖突。
作用域範圍一般是:全局(global)->局部(local)->内置(build-in)
先看看全局和局部變量:
>>> a = 2
... b = 3
>>> a
2
>>> b
nameerror: name 'b' is not defined
a變量的作用域是整個代碼中有效,稱為全局變量,也就是說一段代碼最開始定義的變量。
b變量的作用域在函數内部,也就是局部變量,在函數外是不可引用的。
這麼一來,全局變量與局部變量即使名字一樣也不沖突。
如果函數内部的變量也能在全局引用,需要使用global聲明:
... global b
抛出異常,說明一個問題,當函數沒引用使用,裡面的代碼塊是沒有解釋的。
使用global聲明變量後外部是可以調用函數内部的變量的。
<b>5.5 嵌套函數</b>
# 不帶參數
... x = 2
... def func2():
... return x
... return func2 # 傳回func2函數
>>> func()()
>>> func2()
nameerror: name 'func2' is not defined
>>> def func():
... x = 2
... global func2
... return x
... return func2
内層函數可以通路外層函數的作用域。内嵌函數隻能被外層函數調用,但也可以使用global聲明全局作用域。
調用内部函數的另一種用法:
# 帶參數
... def func2(b):
... return a * b
>>> f = func(2) # 變量指向函數。是的,變量可以指向函數。
>>> f(5)
10
>>> func(2)(5)
内層函數可以通路外層函數的作用域 。但變量不能重新指派,舉例說明:
... x = 3
... func2()
... return x
... x += 1
file "<stdin>", line 5, in func
file "<stdin>", line 4, in func2
unboundlocalerror: local variable 'x' referenced before assignment
<b>5.6 閉包</b>
“官方”的解釋是:所謂“閉包”,指的是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。
其實,上面嵌套函數就是閉包一種方式:
func是一個函數,裡面又嵌套了一個函數func2,外部函數傳過來的a參數,這個變量會綁定到函數func2。func函數以内層函數func2作為傳回值,然後把func函數存儲到f變量中。當外層函數調用内層函數時,内層函數才會執行(func()()),就建立了一個閉包。
<b>5.7 高階函數</b>
高階函數是至少滿足這兩個任意中的一個條件:
1) 能接受一個或多個函數作為輸入。
2)輸出一個函數。
abs、map、reduce都是高階函數,後面會講解。
其實,上面所講的嵌套函數也是高階函數。
舉例說明下高階函數:
>>> def f(x):
... return x * x
>>> def f2(func, y):
... return func(y)
>>> f2(f, 2)
這裡的f2就是一個高階函數,因為它的第一個參數是一個函數,滿足了第一個條件。
<b>部落格位址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang</b>
qq群:323779636(shell/python運維開發群)
<b>5.8 函數裝飾器</b>
裝飾器(decorator)本身是一個函數,包裝另一個函數或類,它可以讓其他函數在不需要改動代碼情況下動态增加功能,裝飾器傳回的也是一個函數對象。
先舉一個例子,說明下裝飾器的效果,定義兩個函數,分别傳參計算乘積:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def f1(a, b):
print "f1 result: " + str(a * b)
def f2(a, b):
print "f2 result: " + str(a * b)
f1(1, 2)
f2(2, 2)
# python test.py
f1 result: 2
f2 result: 4
跟預期的那樣,列印出了乘積。
如果我想給這兩個函數加一個列印傳入的參數,怎麼辦,應該這樣:
print "f1 parameter: %d %d" %(a, b)
print "f2 parameter: %d %d" %(a, b)
f1 parameter: 1 2
f2 parameter: 2 2
按照所想的列印了傳入的參數,有沒有方法能更簡潔點呢,來看看裝飾器後的效果。
def deco(func):
def f(a, b):
print "%s parameter: %d %d" %(func.__name__, a, b)
return func(a, b)
return f
@deco
可見用裝飾器也實作了上面方法,給要裝飾的函數添加了裝飾器定義的功能,這種方式顯得是不是更簡潔呢!
好,那麼我們繼續深入學習裝飾器用法。
5.8.1 無參數裝飾器
方式1:函裝飾器函數裝飾函數
return func
def f1():
print "hello world!"
myfunc = deco(f1)
myfunc()
方式2:使用文法糖"@"來裝飾函數
f1()
方式1是将一個函數作為參數傳給裝飾器函數。
方式2使用了文法糖,也實作同樣效果。
其實兩種方式結果一樣,方式1需要每次使用裝飾器時要先變量指派下,而方式2使用裝飾器時直接用文法糖"@"引用,會顯得更友善些,實際代碼中一般也都是用文法糖。
5.8.2 帶參數裝飾器
print "function name: %s" % func.__name__ # __name__屬性是擷取函數名,為了說明執行了這個函數
return func(a, b) # 用接受過來的func函數來處理傳過來的參數
print a + b
f1(2, 2)
function name: f1
3)不固定參數
def log(func):
def deco(*args, **kwargs):
print "function name: %s" % func.__name__
return func(*args, **kwargs)
return deco
@log
print "f1() run."
f1(1,2)
f1() run.
4)裝飾器加參數
# 三層函數,調用log函數傳回deco函數,再調用傳回的函數deco,則傳回值是_deco函數
def log(arg):
def deco(func):
def _deco(*args, **kwargs):
print "%s - function name: %s" % (arg, func.__name__)
return func(*args, **kwargs)
return _deco
@log("info")
info - function name: f1
再舉一個例子,給函數輸出字元串帶顔色:
def fontcolor(color):
begin = "\033["
end = "\033[0m"
d = {
'red':'31m',
'green':'32m',
'yellow':'33m',
'blue':'34m'
}
print begin + d[color] + func() + end
@fontcolor("red")
def f():
return "hello world!"
@fontcolor("green")
def f2():
可以看出裝飾器處理方式滿足了高階函數的條件,是以裝飾器也是一種高階函數。
裝飾器優點:靈活給裝飾器增加功能,而不修改函數,提高代碼可重複利用性,增加可讀性。
<b>5.9 匿名函數</b>
匿名函數:定義函數的一種形式,無需定義函數名和語句塊,是以代碼邏輯會受到局限,同時也減少代碼量,增加可讀性。
在python中匿名函數是lambda。
舉例子說明def關鍵字與lambda函數定義函數差別:
# 普通函數
... return "hello world!"
... return a * b
>>> func(2, 2)
# 匿名函數
>>> f = lambda:"hello world!"
>>> f()
>>> f = lambda a, b: a * b # 冒号左邊是函數參數,右邊是傳回值
>>> f(2, 2)
lambda函數一行就寫成一個函數功能,省去定義函數過程,讓代碼更加精簡。
<b>5.10 内置高階函數</b>
<b> 5.10.1 map()</b>
文法:map(function, sequence[, sequence, ...]) -> list
将序列中的元素通過函數處理傳回一個新清單。
例如:
>>> lst = [1,2,3,4,5]
>>> map(lambda x:str(x)+".txt", lst)
['1.txt', '2.txt', '3.txt', '4.txt', '5.txt']
<b> 5.10.2 filter()</b>
文法:filter(function or none, sequence) -> list, tuple, or string
将序列中的元素通過函數處理傳回一個新清單、元組或字元串。
例如:過濾清單中的奇數
>>> filter(lambda x:x%2==0, lst)
[2, 4]
<b> 5.10.3 reduce()</b>
文法:reduce(function, sequence[, initial]) -> value
reduce()是一個二進制運算函數,是以隻接受二進制操作函數。
例如:計算清單總和
>>> reduce(lambda x,y:x+y, lst)
15
先将前兩個元素相加等于3,再把結果與第三個元素相加等于6,以此類推。這就是reduce()函數功能。