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