流程控制
除了前面介紹的 while 語句,Python還更多的流程控制工具。
if語句
>>> x = int(raw_input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
... x = 0
... print 'Negative changed to zero'
... elif x == 0:
... print 'Zero'
... elif x == 1:
... print 'Single'
... else:
... print 'More'
...
More
可能會有零到多個elif部分,else是可選的。關鍵字‘elif‘是‘else if’的縮寫,可避免過深的縮進。 if ... elif ... elif序列用於替代其它語言中的switch或case語句。python中沒有case語言,可以考慮用字典或者elif語句替代。
for語句
Python的for語句針對序列(列表或字符串等)中的子項進行循環,按它們在序列中的順序來進行迭代。
>>> # Measure some strings:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
... print w, len(w)
...
cat 3
window 6
defenestrate 12
在迭代過程中修改迭代序列不安全,可能導致部分元素重複兩次,建議先拷貝:
>>> for w in words[:]: # Loop over a slice copy of the entire list.
... if len(w) > 6:
... words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']
range()函數
內置函數 range()生成等差數值序列:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(10) 生成了一個包含10個值的鏈表,但是不包含最右邊的值。默認從0開始,也可以讓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(-10, -100, 30)
[]
如果迭代時需要索引和值可結合使用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
不過使用enumerate()更方便,參見後面的介紹。
break和continue語句及循環中的else子句
break語句和C中的類似,用於終止當前的for或while循環。
循環可能有else 子句;它在循環迭代完整個列表(對於 for)後或執行條件爲false(對於 while)時執行,但循環break時不會執行。這點和try...else而不是if...else相近。請看查找素數的程序:
>>> 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
continue語句也是從C而來,它表示退出當次循環,繼續執行下次迭代。通常可以用if...else替代,請看查找偶數的實例:
>>> for num in range(2, 10):
... if num % 2 == 0:
... print "Found an even number", num
... continue
... print "Found a number", num
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
pass
pass語句什麼也不做。它語法上需要,但是實際什麼也不做場合,也常用語以後預留以後擴展。例如:
>>> while True:
... pass # Busy-wait for keyboard interrupt (Ctrl+C)
...
>>> class MyEmptyClass:
... pass
...
>>> def initlog(*args):
... pass # Remember to implement this!
...
定義函數
菲波那契數列的函數:
>>> def fib(n): # write Fibonacci series up to n
... """Print a Fibonacci series up to n."""
... a, b = 0, 1
... while a < n:
... print a,
... a, b = b, a+b
...
>>> # Now call the function we just defined:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
關鍵字def引入函數定義,其後有函數名和包含在圓括號中的形式參數。函數體語句從下一行開始,必須縮進的。
函數體的第一行語句可以是可選的字符串文本,即文檔字符串。有些工具通過docstrings 自動生成文檔,或者讓用戶通過代碼交互瀏覽;添加文檔字符串是個很好的習慣。
函數執行時生成符號表用來存儲局部變量。 確切地說,所有函數的變量賦值都存儲在局部符號表。 變量查找的順序,先局部,然後逐級向上,再到全局變量,最後內置名。全局變量可在局部直接飲用,但不能直接賦值(除非用global聲明),儘管他們可以被引用, 因爲python在局部賦值會重新定義一個本地變量。
函數的實際參數在調用時引入局部符號表,也就是說是傳值調用(值總是對象引用, 而不是該對象的值)。
函數定義會在當前符號表內引入函數名。 函數名的值爲用戶自定義函數的類型,這個值可以賦值給其他變量當做函數別名使用。
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
沒有return語句的函數也會返回None。 解釋器一般不會顯示None,除非用print打印。
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
從函數中返回
>>> 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 a < n:
... result.append(a) # see below
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # call it
>>> f100 # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
return語句從函數中返回值,不帶表達式的return返回None。過程結束後也會返回 None 。
語句result.append(b)稱爲調用了列表的方法。方法是屬於對象的函數,如obj.methodename,obj 是個對象(可能是一個表達式),methodname是對象的方法名。不同類型有不同的方法。不同類型可能有同名的方法。append()向鏈表尾部附加元素,等同於 result = result + [b] ,不過更有效。
深入Python函數定義
python的函數參數有三種方式。
默認參數
最常用的方式是給參數指定默認值,調用時就可以少傳參數:
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
while True:
ok = raw_input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
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)
給出所有的參數: ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
in關鍵字測定序列是否包含指定值。
默認值在函數定義時傳入,如下所示:
i = 5
def f(arg=i):
print arg
i = 6
f()
上例顯示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
關鍵字參數
關鍵字參數 的形式: keyword = 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) # 1 positional argument
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
無效調用
parrot() # 沒有必選參數
parrot(voltage=5.0, 'dead') # 關鍵參數後面有非關鍵字參數
parrot(110, voltage=220) # 同一參數重複指定值
parrot(actor='John Cleese') # 不正確的關鍵字參數名
關鍵字參數在位置參數之後,多個關鍵字參數的順序先後無關,一個參數只能指定一次值,報錯實例:
>>> def function(a):
... pass
...
>>> function(0, a=0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'
最後一個如果前有兩個星號(比如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 = sorted(keywords.keys())
for kw in keys:
print kw, ":", keywords[kw]
調用
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper='Michael Palin',
client="John Cleese",
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排序。
任意參數列表
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
參數列表解包
把列表或元組拆分成多個並列的參數。
>>> range(3, 6) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args) # call with arguments unpacked from a list
[3, 4, 5]
同樣的字典可以用兩個星號解包:
>>> def parrot(voltage, state='a stiff', action='voom'):
... print "-- This parrot wouldn't", action,
... print "if you put", voltage, "volts through it.",
... print "E's", state, "!"
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
Lambda表達式
lambda關鍵字可創建短小的匿名函數,函數體只有一行,創建時就可使用。比如求和:lambda a, b: a+b。通常不建議使用:
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
除了返回表達式,lambda還可以用作函數參數。
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')](1)
文檔字符串
文檔字符串的內容和格式建議如下。
第一簡短介紹對象的目的。不能描述對象名和類型等其他地方能找到的信息,首字母要大寫。
如果文檔字符串有多行,第二行爲空行以分隔概述和其他描述。描述介紹調用約定、邊界效應等。
Python解釋器不會從多行文檔字符串中去除縮進,要用工具來處理。約定如下:第一行後的第一個非空行決定了整個文檔的縮進。實例:
>>> 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.
編碼風格
建議遵守PEP8,高可讀性,部分要點如下:
使用4空格縮進,而非tab。
每行不超過79個字符。
使用空行分隔函數和類,以及函數中的大代碼塊。
可能的話,註釋佔一行
使用文檔字符串
操作符前後有空格,逗號後有空格,但是括號兩側無空格。如: a = f(1, 2) + g(3, 4) 。
統一函數和類命名。類名用首字母大寫的駝峯式,比如CamelCase。函數和方法名用小寫和下劃線組成:lower_case_with_underscores。類中使用self。
國際化時不要使用花哨的編碼。
另: https://pypi.python.org/pypi/autopep8能把代碼調整爲符合pep8,https://pypi.python.org/pypi/pep8能檢查是否符合pep8,推薦使用。