Python指南--深入流程控制 (轉)

worldblog發表於2008-01-29
Python指南--深入流程控制 (轉)[@more@]

本節目錄

  • //tut/node6.html#SECTION006100000000000000000">4.1 if

  • s



 
4. 其它流程控制工具

除了前面介紹的 while 語句,Python 還從別的語言中借鑑了一些流程控制功能,並有所改變。

 
4.1 if 語句

也許最有句的語句型別是 if 語句。例如:

>>> x = int(raw_input("Please enter an integer: ")) >>> if x < 0: ... x = 0 ... print 'Negative changed to zero' ... elif x == 0: ... print 'Zero' ... elif x == 1: ... print 'Single' ... else: ... print 'More' ...


可能會有 0 或很多個 elif 部分,else 是可選的。關鍵字“elif ” 是“ else if ”的縮寫,這個可以有效避免過深的縮排。if ... elif ... elif ... 序列用於替代其它語言中的 switch 或 case 語句。

 
4.2 for 語句

Python中的for  語句和你在 C 或 Pascal 中使用的略有不同。通常的迴圈可能會依據一個等差數值步進過程(如Pascal)或由來定義迭代步驟和中止條件(如C),Python 的 for  語句依據任意序列(連結串列或字串)中的子項,按它們在序列中的順序來進行迭代。例如(沒有暗指):

>>> # Measure some strings: ... a = ['cat', 'window', 'defenestrate'] >>> for x in a: ... print x, len(x) ... cat 3 window 6 defenestrate 12


在迭代過程中修改迭代序列不(只有在使用連結串列這樣的可變序列時才會有這樣的情況)。如果你想要修改你迭代的序列(例如,複製選擇項),你可以迭代它的複本。通常使用切片標識就可以很方便的做到這一點:

>>> for x in a[:]: # make a slice copy of the entire list ... if len(x) > 6: a.insert(0, x) ... >>> a ['defenestrate', 'cat', 'window', 'defenestrate']


 
4.3 range() 函式

如果你需要一個數值序列,內建函式range()可能會很有用,它生成一個等差級數連結串列。

>>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


range(10)生成了一個包含10個值的連結串列,它準確的用連結串列的值填充了這個長度為10的列表,所生成的連結串列中不包括範圍中的結束值。也可以讓range操作從另一個數值開始,或者可以指定一個不同的步進值(甚至是負數,有時這也被稱為“步長”):

>>> range(5, 10) [5, 6, 7, 8, 9] >>> range(0, 10, 3) [0, 3, 6, 9] >>> range(-10, -100, -30) [-10, -40, -70]


需要迭代連結串列索引的話,如下所示結合使用range()和len():

>>> a = ['Mary', 'had', 'a', 'little', 'lamb'] >>> for i in range(len(a)): ... print i, a[i] ... 0 Mary 1 had 2 a 3 little 4 lamb


 
4.4 breakcontinue 語句, 以及迴圈中的 else 子句

break語句和C中的類似,用於跳出最近的一級for或while迴圈。

continue 語句是從C中借鑑來的,它表示迴圈繼續下一次迭代。

迴圈可以有一個else子句;它在迴圈迭代完整個列表(對於for)或執行條件為false(對於while)時執行,但迴圈被break中止的情況下不會執行。以下搜尋素數的示例演示了這個子句:

>>> for n in range(2, 10): ... for x in range(2, n): ... if n % x == 0: ... print n, 'equals', x, '*', n/x ... break ... else: ... # loop fell through without finding a factor ... print n, 'is a prime number' ... 2 is a prime number 3 is a prime number 4 equals 2 * 2 5 is a prime number 6 equals 2 * 3 7 is a prime number 8 equals 2 * 4 9 equals 3 * 3


 
4.5 pass 語句

pass 語句什麼也不做。它用於那些語法上必須要有什麼語句,但程式上什麼也不要做的場合,例如:

>>> while True: ... pass # Busy-wait for keyboard interrupt ...


 
4.6 定義函式

我們可以編寫一個函式來生成有給定上界的菲波那契數列:

>>> def fib(n): # write Fibonacci series up to n ... """Print a Fibonacci series up to n.""" ... a, b = 0, 1 ... while b < n: ... print b, ... a, b = b, a+b ... >>> # Now call the function we just defined: ... fib(2000) 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597


關鍵字def 引入了一個函式定義。在其後必須跟有函式名和包括形式引數的圓括號。函式體語句從下一行開始,必須是縮排的。函式體的第一行可以是一個字串值,這個字串是該函式的 文件字串,也可稱作docstring。 

有些文件字串工具可以線上處理或列印文件,或讓使用者互動的瀏覽程式碼;在你的程式碼中加入文件字串是一個好的作法,應該養成習慣。

函式時會為區域性變數引入一個新的符號表。所有的區域性變數都在這個區域性符號表中。引用引數時,會先從區域性符號表中查詢,然後是全域性符號表,然後是內建命名錶。因此,全域性引數雖然可以被引用,但它們不能在函式中直接賦值(除非它們用global語句命名)。

函式引用的實際引數在函式呼叫時引入區域性符號表,因此,實參總是傳值呼叫(這裡的總是一個引用,而不是該物件的值)。 一個函式被另一個函式呼叫時,一個新的區域性符號表在呼叫過程中被建立。

函式定義在當前符號表中引入函式名。作為使用者定義函式,函式名有一個為直譯器認可的型別值。這個值可以賦給其它命名,使其能句做為一個函式來使用。這就像一個重新命名機制:

>>> fib >>> f = fib >>> f(100) 1 1 2 3 5 8 13 21 34 55 89


你可能認為fib不是一個函式(function),而是一個過程(procedure)。Python和C一樣,過程只是一個沒有返回值的函式。實際上,從技術上講,過程也有一個返回值,雖然是一個不討人喜歡的。這個值被稱為 None (這是一個內建命名)。如果一個值只是None的話,通常直譯器不會寫一個None出來,如果你真想要看它的話,可以這樣做:

>>> print fib(0) None


以下示列演示瞭如何從函式中返回一個包含菲波那契數列的數值連結串列,而不是列印它:

>>> def fib2(n): # return Fibonacci series up to n ... """Return a list containing the Fibonacci series up to n.""" ... result = [] ... a, b = 0, 1 ... while b < n: ... result.append(b) # see below ... a, b = b, a+b ... return result ... >>> f100 = fib2(100) # call it >>> f100 # write the result [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]


和以前一樣,這個例子演示了一些新的Python功能:

  • return語句從函式中返回一個值,不帶的return返回None。過程結束後也會返回None。

  • 語句result.append(b)稱為連結串列物件result的一個方法(method)。方法是一個“屬於”某個物件的函式,它被命名為obj.methodename,這裡的obj是某個物件(可能是一個表示式),methodename是某個在該物件型別定義中的方法的命名。 不同的型別定義不同的方法。不同型別可能有同樣名字的方法,但不會混淆。(當你定義自己的物件型別和方法時,可能會出現這種情況,本指南後面的章節會介紹如何使用)。示例中演示的append()方法由連結串列物件定義,它向連結串列中加入一個新元素。在示例中它等同於"result = result + [b]",不過更高。

 
4.7 深入函式定義

有時需要定義引數個數可變的函式。有三個方法可以做到,我們可以組合使用它們。

 
4.7.1 引數預設值

最有用的形式是給一個或多個引數指定預設值。這樣建立的函式可以在呼叫時使用更少的引數。

def ask_ok(prompt, retries=4, complaint='Yes or no, please!'): while True: ok = raw_input(prompt) if ok in ('y', 'ye', 'yes'): return 1 if ok in ('n', 'no', 'nop', 'nope'): return 0 retries = retries - 1 if retries < 0: raise IOError, 'refusenik user' print complaint


這個函式還可以用以下的方式呼叫:ask_ok('Do you really want to quit?'),或者像這樣:ask_ok('OK to overwrite the file?', 2)

預設值在函式定義段被解析,如下所示:

i = 5 def f(arg=i): print arg i = 6 f()


will print 5.

重要警告:預設值只會解析一次。當預設值是一個可變物件,諸如連結串列、字典或大部分類例項時,會產生一些差異。例如,以下函式在後繼的呼叫中會積累它的引數值:

def f(a, L=[]): L.append(a) return L print f(1) print f(2) print f(3)


這會列印出:

[1] [1, 2] [1, 2, 3]


如果你不想在不同的函式呼叫之間共享引數預設值,可以如下面的例項一樣編寫函式:

def f(a, L=None): if L is None: L = [] L.append(a) return L


 
4.7.2 引數關鍵字

函式可以透過引數關鍵字的形式來呼叫,形如“key = value”。例如,以下的函式:

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'): print "-- This parrot wouldn't", action, print "if you put", voltage, "Volts through it." print "-- Lovely plumage, the", type print "-- It's", state, "!"


可以用以下的任一方法呼叫:

parrot(1000) parrot(action = 'VOOOOOM', voltage = 1000000) parrot('a thousand', state = 'pushing up the daisies') parrot('a million', 'bereft of life', 'jump')


不過以下幾種呼叫是無效的:

parrot() # required argument missing(缺少必要引數) parrot(voltage=5.0, 'dead') # non-keyword argument following keyword(在關鍵字後面有非關鍵字引數) parrot(110, voltage=220) # duplicate value for argument(對引數進行了重複賦值) parrot(actor='John Cleese') # unknown keyword(未知關鍵字)


通常,引數列表中的每一個關鍵字都必須來自於形式引數,每個引數都有對應的關鍵字。形式引數有沒有預設值並不重要。實際引數不能一次賦多個值--形式引數不能在同一次呼叫中同時使用位置和關鍵字繫結值。這裡有一個例子演示了在這種下所出現的失敗情況:

>>> def function(a): ... pass ... >>> function(0, a=0) Traceback (most recent call last): File "", line 1, in ? TypeError: function() got multiple values for keyword argument 'a'


引入一個形如 **name 的引數時,它接收一個字典,該字典包含了所有未出現在形式引數列表中的關鍵字引數。這裡可能還會組合使用一個形如 *name 的形式引數,它接收一個拓撲(下一節中會詳細介紹),包含了所有沒有出現在形式引數列表中的引數值。(*name 必須在 **name 之前出現) 例如,我們這樣定義一個函式:

def cheeseshop(kind, *arguments, **keywords): print "-- Do you have any", kind, '?' print "-- I'm sorry, we're all out of", kind for arg in arguments: print arg print '-'*40 keys = keywords.keys() keys.sort() for kw in keys: print kw, ':', keywords[kw]


它可以像這樣呼叫:

cheeseshop('Limburger', "It's very runny, sir.", "It's really very, VERY runny, sir.", client='John Cleese', shopkeeper='Michael Palin', sketch='Cheese Shop Sketch')


當然它會按如下內容列印:

-- Do you have any Limburger ? -- I'm sorry, we're all out of Limburger It's very runny, sir. It's really very, VERY runny, sir. ---------------------------------------- client : John Cleese shopkeeper : Michael Palin sketch : Cheese Shop Sketch


注意sort()方法在關鍵字字典內容列印前被呼叫,否則的話,列印引數時的順序是未定義的。

 
4.7.3 可變參數列

最後,一個最不常用的選擇是可以讓函式呼叫可變個數的引數。這些引數被包裝進一個拓撲。在這些可變個數的引數之前,可以有零到多個普通的引數:

def fprintf(file, format, *args): file.write(format % args)


 
4.7.4 Lambda 形式

出於適當的需要,有幾種通常在功能性語言和Lisp中出現的功能加入到了Python。透過lambda關鍵字,可以建立很小的隱式函式(誰能告訴我,這個anonymous functions還可以怎麼講?--譯者)這裡有一個函式返回它的兩個引數的和:“lambda a, b: a+b” Lambda 形式可以用於任何需要的函式物件。出於語法限制,它們只能有一個單獨的表示式。語義上講,它們只是普通函式定義中的一個語法技巧。類似於巢狀函式定義,lambda形式可以從包含範圍內引用變數:

>>> def make_incrementor(n): ... return lambda x: x + n ... >>> f = make_incrementor(42) >>> f(0) 42 >>> f(1) 43


 
4.7.5 文件字串

這裡介紹文件字串的概念和格式。

第一行應該是關於物件用途的簡介。簡短起見,不用明確的陳述物件名或型別,因為它們可以從別的途徑瞭解到(除非這個名字碰巧就是描述這個函式操作的動詞)。這一行應該以大寫字母開頭,以句號結尾。

如果文件字串有多行,第二行應該空出來,與接下來的詳細描述明確分隔。接下來的文件應該有一或多段描述物件的呼叫約定、邊界效應等。

Python的直譯器不會從多行的文件字串中去除縮排,所以必要的時候應當自己清除縮排。這符合通常的習慣。第一行之後的第一個非空行決定了整個文件的縮排格式。(我們不用第一行是因為它通常緊靠著起始的引號,縮排格式顯示的不清楚。)留白“相當於”是字串的起始縮排。每一行都不應該有縮排,如果有縮排的話,所有的留白都應該清除掉。相當於留白就是驗證後的製表符擴充套件(通常是8個空格)。(這一段譯得不通,有疑問的讀者請參見原文--譯者)

以下是一個多行文件字串的示例:

>>> def my_function(): ... """Do nothing, but document it. ... ... No, really, it doesn't do anything. ... """ ... pass ... >>> print my_function.__doc__ Do nothing, but document it. No, really, it doesn't do anything.




腳註

... 物件)。
實際上,說是呼叫物件引用更合適,因為如果傳入一個可變物件,呼叫者可以得到被呼叫者產生的任何改變(如連結串列中插入了子項)。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-998691/,如需轉載,請註明出處,否則將追究法律責任。

相關文章