Python學習筆記——基礎語法(下)

續上文《Python學習筆記——基礎語法(上)

寫在前面:本文適合有高級編程語言基礎的讀者閱讀。


一、Dict和Set類型

1、什麼是dict

      Python的 dict表示一組鍵值對。用 dict 表示“名字”-“成績”的查找表如下:

d = {
    'Adam': 95,
    'Lisa': 85,
    'Bart': 59
}

我們把名字稱爲key,對應的成績稱爲value,dict就是通過key 來查找 value

花括號 {} 表示這是一個dict,然後按照 key: value, 寫出來即可。最後一個 key: value 的逗號可以省略。

由於dict也是集合,len() 函數可以計算任意集合的大小:

>>> len(d)
3

注意: 一個 key-value 算一個,因此,dict大小爲3。

2、訪問dict

     可以簡單地使用 d[key] 的形式來查找對應的 value,這和 list 很像,不同之處是,list 必須使用索引返回對應的元素,而dict使用key:

>>> print d['Adam']
95
>>> print d['Paul']
Traceback (most recent call last):
  File "index.py", line 11, in <module>
    print d['Paul']
KeyError: 'Paul'

注意: 通過 key 訪問 dict 的value,只要 key 存在,dict就返回對應的value。如果key不存在,會直接報錯:KeyError。

要避免 KeyError 發生,有兩個辦法:

一是先判斷一下 key 是否存在,用 in 操作符:

if 'Paul' in d:
    print d['Paul']

如果 'Paul' 不存在,if語句判斷爲False,自然不會執行 print d['Paul'] ,從而避免了錯誤。

二是使用dict本身提供的一個 get 方法,在Key不存在的時候,返回None:

>>> print d.get('Bart')
59
>>> print d.get('Paul')
None

3、dict的特點

  • 查找速度快無論dict有10個元素還是10萬個元素,查找速度都一樣。而list的查找速度隨着元素增加而逐漸下降。不過dict的查找速度快不是沒有代價的,dict的缺點是佔用內存大,還會浪費很多內容,list正好相反,佔用內存小,但是查找速度慢。另外,由於dict是按 key 查找,所以,在一個dict中,key不能重複
  • 無序。這和list不一樣:
    d = {
        'Adam': 95,
        'Lisa': 85,
        'Bart': 59
    }

    當我們試圖打印這個dict時:

    >>> print d
    {'Lisa': 85, 'Adam': 95, 'Bart': 59}
    打印的順序不一定是我們創建時的順序,而且,不同的機器打印的順序都可能不同,這說明dict內部是無序的,不能用dict存儲有序的集合。
  • 作爲key的元素必須不可變。Python的基本類型如字符串、整數、浮點數都是不可變的,都可以作爲 key。但是list是可變的,就不能作爲 key。不可變這個限制僅作用於key,value是否可變無所謂。

4、更新dict

      dict是可變的,也就是說,我們可以隨時往dict中添加新的 key-value。比如已有dict:

d = {
    'Adam': 95,
    'Lisa': 85,
    'Bart': 59
}

要把新同學'Paul'的成績 72 加進去,用賦值語句:

>>> d['Paul'] = 72

再看看dict的內容:

>>> print d
{'Lisa': 85, 'Paul': 72, 'Adam': 95, 'Bart': 59}

如果 key 已經存在,則賦值會用新的 value 替換掉原來的 value:

>>> d['Bart'] = 60
>>> print d
{'Lisa': 85, 'Paul': 72, 'Adam': 95, 'Bart': 60}

5、遍歷dict

      由於dict也是一個集合,所以,遍歷dict和遍歷list類似,都可以通過 for 循環實現。

直接使用for循環可以遍歷 dict 的 key,比如打印出 name: score

d = {
    'Adam': 95,
    'Lisa': 85,
    'Bart': 59
}
for name in d:
    print name,":",d[name]

由於通過 key 可以獲取對應的 value,因此,在循環體內,可以獲取到value的值。

6、什麼是Set

      dict的作用是建立一組 key 和一組 value 的映射關係,dict的key是不能重複的。有的時候,我們只想要 dict 的 key,不關心 key 對應的 value,目的就是保證這個集合的元素不會重複,這時,set就派上用場了。set 持有一系列元素,這一點和 list 很像,但是set的元素沒有重複,而且是無序的,這點和 dict 的 key很像。

創建 set 的方式是調用 set() 並傳入一個 list,list的元素將作爲set的元素:

>>> s = set(['A', 'B', 'C'])

可以查看 set 的內容:

>>> print s
set(['A', 'C', 'B'])

請注意,上述打印的形式類似 list, 但它不是 list,仔細看還可以發現,打印的順序和原始 list 的順序有可能是不同的,因爲set內部存儲的元素是無序的。

因爲set不能包含重複的元素,所以,當我們傳入包含重複元素的 list 會怎麼樣呢?

>>> s = set(['A', 'B', 'C', 'C'])
>>> print s
set(['A', 'C', 'B'])
>>> len(s)
3

結果顯示,set會自動去掉重複的元素,原來的list有4個元素,但set只有3個元素。

7、訪問set

      由於set存儲的是無序集合,所以我們沒法通過索引來訪問。訪問 set中的某個元素實際上就是判斷一個元素是否在set中。例如,存儲了班裏同學名字的set:

>>> s = set(['Adam', 'Lisa', 'Bart', 'Paul'])

我們可以用 in 操作符判斷:

Bart是該班的同學嗎?

>>> 'Bart' in s
True

Bill是該班的同學嗎?

>>> 'Bill' in s
False

bart是該班的同學嗎?

>>> 'bart' in s
False

看來大小寫很重要,'Bart' 和 'bart'被認爲是兩個不同的元素。

8、set的特點

  • set的內部結構和dict很像,唯一區別是不存儲value,因此,判斷一個元素是否在set中速度很快。
  • set存儲的元素和dict的key類似,必須是不變對象,因此,任何可變對象是不能放入set中的。
  • 最後,set存儲的元素也是沒有順序的。

set的這些特點,可以應用在哪些地方呢?星期一到星期日可以用字符串'MON', 'TUE', ... 'SUN'表示。假設我們讓用戶輸入星期一至星期日的某天,如何判斷用戶的輸入是否是一個有效的星期呢?可以用 if 語句判斷,但這樣做非常繁瑣:

x = '???' # 用戶輸入的字符串
if x!= 'MON' and x!= 'TUE' and x!= 'WED' ... and x!= 'SUN':
    print 'input error'
else:
    print 'input ok'

注意:if 語句中的...表示沒有列出的其它星期名稱,測試時,請輸入完整。

如果事先創建好一個set,包含'MON' ~ 'SUN':

weekdays = set(['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'])

再判斷輸入是否有效,只需要判斷該字符串是否在set中:

x = '???' # 用戶輸入的字符串
if x in weekdays:
    print 'input ok'
else:
    print 'input error'

這樣一來,代碼就簡單多了。

9、遍歷set

      由於 set 也是一個集合,所以,遍歷 set 和遍歷 list 類似,都可以通過 for 循環實現。直接使用 for 循環可以遍歷 set 的元素:

>>> s = set(['Adam', 'Lisa', 'Bart'])
>>> for name in s:
...     print name
... 
Lisa
Adam
Bart

注意: 觀察 for 循環在遍歷set時,元素的順序和list的順序很可能是不同的,而且不同的機器上運行的結果也可能不同。

再比如,用for 循環遍歷如下的set,打印出 name: score 來。
參考代碼:
s = set([('Adam', 95), ('Lisa', 85), ('Bart', 59)])
s = set([('Adam', 95), ('Lisa', 85), ('Bart', 59)])
for x in s:  #this x is tuple
    print x[0],":",x[1]
運行結果:
Lisa : 85
Adam : 95
Bart : 59

10、更新set

      由於set存儲的是一組不重複的無序元素,因此,更新set主要做兩件事:一是把新的元素添加到set中,二是把已有元素從set中刪除。添加元素時,用set的add()方法:

>>> s = set([1, 2, 3])
>>> s.add(4)
>>> print s
set([1, 2, 3, 4])

如果添加的元素已經存在於set中,add()不會報錯,但是不會加進去了:

>>> s = set([1, 2, 3])
>>> s.add(3)
>>> print s
set([1, 2, 3])

刪除set中的元素時,用set的remove()方法:

>>> s = set([1, 2, 3, 4])
>>> s.remove(4)
>>> print s
set([1, 2, 3])

如果刪除的元素不存在set中,remove()會報錯:

>>> s = set([1, 2, 3])
>>> s.remove(4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 4

所以用add()可以直接添加,而remove()前需要判斷

二、函數

1、什麼是函數

       函數就是最基本的一種代碼抽象的方式。Python不但能非常靈活地定義函數,而且本身內置了很多有用的函數,可以直接調用。

2、調用函數

      要調用一個函數,需要知道函數名稱參數,比如求絕對值的函數 abs,它接收一個參數。

可以直接從Python的官方網站查看文檔:
http://docs.python.org/2/library/functions.html#abs

也可以在交互式命令行通過 help(abs) 查看abs函數的幫助信息。

調用 abs 函數:

>>> abs(100)
100
>>> abs(-20)
20
>>> abs(12.34)
12.34

調用函數的時候,如果傳入的參數數量不對,會報TypeError的錯誤,並且Python會明確地告訴你:abs()有且僅有1個參數,但給出了兩個:

>>> abs(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: abs() takes exactly one argument (2 given)

如果傳入的參數數量是對的,但參數類型不能被函數所接受,也會報TypeError的錯誤,並且給出錯誤信息:str是錯誤的參數類型:

>>> abs('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'

而比較函數 cmp(x, y) 就需要兩個參數,如果x<y,返回 -1,如果 x==y,返回 0,如果x>y,返回 1

>>> cmp(1, 2)
-1
>>> cmp(2, 1)
1
>>> cmp(3, 3)
0

Python內置的常用函數還包括數據類型轉換函數,比如   int()函數可以把其他數據類型轉換爲整數:

>>> int('123')
123
>>> int(12.34)
12

str()函數把其他類型轉換成 str:

>>> str(123)
'123'
>>> str(1.23)
'1.23'

實例:sum()函數接受一個list作爲參數,並返回list所有元素之和。請計算 1*1 + 2*2 + 3*3 + ... + 100*100。

參考代碼:

L = [i*i for i in range(1,101)]
print sum(L)

3、編寫函數

      在Python中,定義一個函數要使用 def語句,依次寫出函數名括號、括號中的參數冒號:,然後,在縮進塊中編寫函數體,函數的返回值用 return 語句返回。

我們以自定義一個求絕對值的 my_abs 函數爲例:

def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x

請注意,函數體內部的語句在執行時,一旦執行到return時,函數就執行完畢,並將結果返回。因此,函數內部通過條件判斷和循環可以實現非常複雜的邏輯。

如果沒有return語句,函數執行完畢後也會返回結果,只是結果爲 None。return None可以簡寫爲return。

4、函數返回多值

      比如在遊戲中經常需要從一個點移動到另一個點,給出座標、位移和角度,就可以計算出新的座標:

# math包提供了sin()和 cos()函數,我們先用import引用它:

import math
def move(x, y, step, angle):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny

這樣我們就可以同時獲得返回值:

>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print x, y
151.961524227 70.0

但其實這只是一種假象,Python函數返回的仍然是單一值:

>>> r = move(100, 100, 60, math.pi / 6)
>>> print r
(151.96152422706632, 70.0)

用print打印返回結果,原來返回值是一個tuple

但是,在語法上,返回一個tuple可以省略括號,而多個變量可以同時接收一個tuple,按位置賦給對應的值,所以,Python的函數返回多值其實就是返回一個tuple,但寫起來更方便。

5、遞歸函數

      在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞歸函數。舉個例子,我們來計算階乘n! = 1 * 2 * 3 * ... * n,用函數 fact(n)表示,可以看出:

fact(n) = n! = 1 * 2 * 3 * ... * (n-1) * n = (n-1)! * n = fact(n-1) * n

所以,fact(n)可以表示爲 n * fact(n-1),只有n=1時需要特殊處理。於是,fact(n)用遞歸的方式寫出來就是:

def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)
上面就是一個遞歸函數。

遞歸函數的優點是定義簡單,邏輯清晰。理論上,所有的遞歸函數都可以寫成循環的方式,但循環的邏輯不如遞歸清晰。

使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。可以試試計算 fact(10000)。

6、定義默認參數

      定義函數的時候,還可以有默認參數。例如Python自帶的 int() 函數,其實就有兩個參數,我們既可以傳一個參數,又可以傳兩個參數:

>>> int('123')
123
>>> int('123', 8)
83

int()函數的第二個參數是轉換進制,如果不傳,默認是十進制 (base=10),如果傳了,就用傳入的參數。

可見,函數的默認參數的作用是簡化調用,你只需要把必須的參數傳進去。但是在需要的時候,又可以傳入額外的參數來覆蓋默認參數值。

我們來定義一個計算 x 的N次方的函數:

def power(x, n):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

假設計算平方的次數最多,我們就可以把 n 的默認值設定爲 2:

def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

這樣一來,計算平方就不需要傳入兩個參數了:

>>> power(5)
25

由於函數的參數按從左到右的順序匹配,所以默認參數只能定義在必需參數的後面:

# OK:
def fn1(a, b=1, c=2):
    pass
# Error:
def fn2(a=1, b):
    pass

再比如,定義一個 greet() 函數,它包含一個默認參數,如果沒有傳入,打印 'Hello, world.',如果傳入,打印 'Hello, xxx.'

參考代碼:

def greet(n='world'):
    print 'Hello,' + n + '.'

greet()
greet('Bart')

運行結果

Hello,world.
Hello,Bart.

7、定義可變參數

     如果想讓一個函數能接受任意個參數,我們就可以定義一個可變參數:

def fn(*args):
    print args

可變參數的名字前面有個 號,我們可以傳入0個、1個或多個參數給可變參數:

>>> fn()
()
>>> fn('a')
('a',)
>>> fn('a', 'b')
('a', 'b')
>>> fn('a', 'b', 'c')
('a', 'b', 'c')

可變參數也不是很神祕,Python解釋器會把傳入的一組參數組裝成一個tuple傳遞給可變參數,因此,在函數內部,直接把變量args 看成一個 tuple 就好了。

定義可變參數的目的也是爲了簡化調用。假設我們要計算任意個數的平均值,就可以定義一個可變參數:

def average(*args):
    ...

這樣,在調用的時候,可以這樣寫:

>>> average()
0
>>> average(1, 2)
1.5
>>> average(1, 2, 2, 3, 4)
2.4

比如,編寫接受可變參數的 average() 函數。

參考代碼:

def average(*args):
    if args:
        return sum(args) * 1.0 / len(args)
    else:
        return 0.0

print average()
print average(1, 2)
print average(1, 2, 2, 3, 4)

運行結果:

0.0
1.5
2.4

三、切片

1、對list進行切片

      取一個list的部分元素是非常常見的操作。比如,一個list如下:

>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']

取前3個元素,應該怎麼做?笨辦法:

>>> [L[0], L[1], L[2]]
['Adam', 'Lisa', 'Bart']

之所以是笨辦法是因爲擴展一下,取前N個元素就沒轍了。取前N個元素,也就是索引爲0-(N-1)的元素,可以用循環:

>>> r = []
>>> n = 3
>>> for i in range(n):
...     r.append(L[i])
... 
>>> r
['Adam', 'Lisa', 'Bart']

對這種經常取指定索引範圍的操作,用循環十分繁瑣,因此,Python提供了切片(Slice)操作符,能大大簡化這種操作。對應上面的問題,取前3個元素,用一行代碼就可以完成切片:

>>> L[0:3]
['Adam', 'Lisa', 'Bart']

L[0:3]表示,從索引0開始取,直到索引3爲止,但不包括索引3。即索引0,1,2,正好是3個元素。如果第一個索引是0,還可以省略:

>>> L[:3]
['Adam', 'Lisa', 'Bart']

也可以從索引1開始,取出2個元素出來:

>>> L[1:3]
['Lisa', 'Bart']

只用一個 : ,表示從頭到尾:

>>> L[:]
['Adam', 'Lisa', 'Bart', 'Paul']

因此,L[:]實際上複製出了一個新list。

切片操作還可以指定第三個參數:

>>> L[::2]
['Adam', 'Bart']

第三個參數表示每N個取一個,上面的 L[::2] 會每兩個元素取出一個來,也就是隔一個取一個。

把list換成tuple,切片操作完全相同,只是切片的結果也變成了tuple。

實例:range()函數可以創建一個數列:

>>> range(1, 101)
[1, 2, 3, ..., 100]

請利用切片,取出:

1. 前10個數;
2. 3的倍數;
3. 不大於50的5的倍數。

參考代碼:

L = range(1, 101)

print L[:10]
print L[2::3]
print L[4:50:5]

運行結果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50]

2、倒序切片

      對於list,既然Python支持L[-1]取倒數第一個元素,那麼它同樣支持倒數切片,試試:

>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']

>>> L[-2:]
['Bart', 'Paul']

>>> L[:-2]
['Adam', 'Lisa']

>>> L[-3:-1]
['Lisa', 'Bart']

>>> L[-4:-1:2]
['Adam', 'Bart']

記住倒數第一個元素的索引是-1。倒序切片包含起始索引,不包含結束索引。

實例:利用倒序切片對 1 - 100 的數列取出:

* 最後10個數;

* 最後10個5的倍數。

參考代碼:

L = range(1, 101)
print L[-10:]
print L[-46::5]

運行結果:

[91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
[55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
第二問可用更簡單的辦法,因爲切片可以嵌套,print L[-46::5] 不是很靈活的方法,得通過計算得知起始座標。若使用 print L[4::5][-10:] 先獲得5的倍數,再取後10個,則效果更好。

3、對字符串切片

      字符串 'xxx'和 Unicode字符串 u'xxx'也可以看成是一種list,每個元素就是一個字符。因此,字符串也可以用切片操作,只是操作結果仍是字符串:

>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[-3:]
'EFG'
>>> 'ABCDEFG'[::2]
'ACEG'

在很多編程語言中,針對字符串提供了很多各種截取函數,其實目的就是對字符串切片。Python沒有針對字符串的截取函數,只需要切片一個操作就可以完成,非常簡單。

實例:字符串有個方法 upper() 可以把字符變成大寫字母:
>>> 'abc'.upper()
'ABC'

但它會把所有字母都變成大寫。請設計一個函數,它接受一個字符串,然後返回一個僅首字母變成大寫的字符串。

提示:利用切片操作簡化字符串操作。

參考代碼:

def firstCharUpper(s):
    return s[0].upper()+s[1:]

print firstCharUpper('hello')
print firstCharUpper('sunday')
print firstCharUpper('september')

運行結果:

Hello
Sunday
September

四、迭代

1、什麼是迭代

      在Python中,如果給定一個listtuple,我們可以通過for循環來遍歷這個list或tuple,這種遍歷我們成爲迭代(Iteration)。

在Python中,迭代是通過 for ... in 來完成的,而很多語言比如C或者Java,迭代list是通過下標完成的,比如Java代碼:

for (i=0; i<list.length; i++) {
    n = list[i];
}

可以看出,Python的for循環抽象程度要高於Java的for循環。因爲 Python 的 for循環不僅可以用在list或tuple上,還可以作用在其他任何可迭代對象上。因此,迭代操作就是對於一個集合,無論該集合是有序還是無序,我們用 for 循環總是可以依次取出集合的每一個元素。

注意: 集合是指包含一組元素的數據結構,我們已經介紹的包括:
1. 有序集合:list,tuple,str和unicode;
2. 無序集合:set
3. 無序集合並且具有 key-value 對:dict

而迭代是一個動詞,它指的是一種操作,在Python中,就是 for 循環。迭代與按下標訪問數組最大的不同是,後者是一種具體的迭代實現方式,而前者只關心迭代結果,根本不關心迭代內部是如何實現的。

2、索引迭代

      Python中,迭代永遠是取出元素本身,而非元素的索引。對於有序集合,元素確實是有索引的。有的時候,我們確實想在 for 循環中拿到索引,怎麼辦?

方法是使用 enumerate() 函數

>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']
>>> for index, name in enumerate(L):
...     print index, '-', name
... 
0 - Adam
1 - Lisa
2 - Bart
3 - Paul

使用 enumerate() 函數,我們可以在for循環中同時綁定索引index和元素name。但是,這不是 enumerate() 的特殊語法。實際上,enumerate() 函數把:

['Adam', 'Lisa', 'Bart', 'Paul']

變成了類似:

[(0, 'Adam'), (1, 'Lisa'), (2, 'Bart'), (3, 'Paul')]

因此,迭代的每一個元素實際上是一個tuple:

for t in enumerate(L):
    index = t[0]
    name = t[1]
    print index, '-', name

如果我們知道每個tuple元素都包含兩個元素,for循環又可以進一步簡寫爲:

for index, name in enumerate(L):
    print index, '-', name

這樣不但代碼更簡單,而且還少了兩條賦值語句。

可見,索引迭代也不是真的按索引訪問,而是由 enumerate() 函數自動把每個元素變成 (index, element) 這樣的tuple,再迭代,就同時獲得了索引和元素本身。

3、迭代dict的value

      我們已經瞭解了dict對象本身就是可迭代對象,用 for 循環直接迭代 dict,可以每次拿到dict的一個key。如果我們希望迭代 dict 對象的value,應該怎麼做?dict 對象有一個 values() 方法,這個方法把dict轉換成一個包含所有value的list,這樣,我們迭代的就是 dict的每一個 value:

d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
print d.values()
# [85, 95, 59]
for v in d.values():
    print v
# 85
# 95
# 59

如果仔細閱讀Python的文檔,還可以發現,dict除了values()方法外,還有一個 itervalues() 方法,用 itervalues() 方法替代 values() 方法,迭代效果完全一樣:

d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
print d.itervalues()
# <dictionary-valueiterator object at 0x106adbb50>
for v in d.itervalues():
    print v
# 85
# 95
# 59

那這兩個方法有何不同之處呢?

1. values() 方法實際上把一個 dict 轉換成了包含 value 的list。

2. 但是 itervalues() 方法不會轉換,它會在迭代過程中依次從 dict 中取出 value,所以 itervalues() 方法比 values() 方法節省了生成 list 所需的內存

3. 打印 itervalues() 發現它返回一個 <dictionary-valueiterator> 對象,這說明在Python中,for 循環可作用的迭代對象遠不止 list,tuple,str,unicode,dict等,任何可迭代對象都可以作用於for循環,而內部如何迭代我們通常並不用關心。

如果一個對象說自己可迭代,那我們就直接用 for 循環去迭代它,可見,迭代是一種抽象的數據操作,它不對迭代對象內部的數據有任何要求。

4、迭代dict的key和value

      我們瞭解瞭如何迭代 dictkeyvalue,那麼,在一個 for 循環中,能否同時迭代 key和value?答案是肯定的。

首先,我們看看 dict 對象的 items() 方法返回的值:

>>> d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
>>> print d.items()
[('Lisa', 85), ('Adam', 95), ('Bart', 59)]

可以看到,items() 方法把dict對象轉換成了包含tuple的list,我們對這個list進行迭代,可以同時獲得key和value:

>>> for key, value in d.items():
...     print key, ':', value
... 
Lisa : 85
Adam : 95
Bart : 59

和 values() 有一個 itervalues() 類似, items() 也有一個對應的 iteritems(),iteritems() 不把dict轉換成list,而是在迭代過程中不斷給出 tuple,所以, iteritems() 不佔用額外的內存

實例:根據dict:d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59, 'Paul': 74 }。打印出 name : score,最後再打印出平均分 average : score。

參考代碼:

d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59, 'Paul': 74 }

sum = 0.0
for k, v in d.items():
    sum = sum + v
    print k,':',v
print 'average', ':', sum / 4

運行結果:

Lisa : 85
Paul : 74
Adam : 95
Bart : 59
average : 78.25

五、列表生成式

1、生成列表

      要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],我們可以用range(1, 11):
>>> range(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎麼做?方法一是循環:

>>> L = []
>>> for x in range(1, 11):
...    L.append(x * x)
... 
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

但是循環太繁瑣,而列表生成式則可以用一行語句代替循環生成上面的list:

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

這種寫法就是Python特有的列表生成式。利用列表生成式,可以以非常簡潔的代碼生成 list

寫列表生成式時,把要生成的元素 x * x 放到前面,後面跟 for 循環,就可以把list創建出來,十分有用,多寫幾次,很快就可以熟悉這種語法。

實例:利用列表生成式生成列表 [1x2, 3x4, 5x6, 7x8, ..., 99x100]

參考代碼:

提示:range(1, 100, 2) 可以生成list [1, 3, 5, 7, 9,...]

print [x * (x + 1) for x in range(1, 100, 2)]

運行結果:

[2, 12, 30, 56, 90, 132, 182, 240, 306, 380, 462, 552, 650, 756, 870, 992, 1122, 1260, 1406, 1560, 1722, 1892, 2070, 2256, 2450, 2652, 2862, 3080, 3306, 3540, 3782, 4032, 4290, 4556, 4830, 5112, 5402, 5700, 6006, 6320, 6642, 6972, 7310, 7656, 8010, 8372, 8742, 9120, 9506, 9900]

2、複雜表達式

      使用for循環的迭代不僅可以迭代普通的list,還可以迭代dict。假設有如下的dict:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }

完全可以通過一個複雜的列表生成式把它變成一個 HTML 表格:

tds = ['<tr><td>%s</td><td>%s</td></tr>' % (name, score) for name, score in d.iteritems()]
print '<table>'
print '<tr><th>Name</th><th>Score</th><tr>'
print '\n'.join(tds)
print '</table>'

注:字符串可以通過 % 進行格式化,用指定的參數替代 %s。字符串的join()方法可以把一個 list 拼接成一個字符串。

把打印出來的結果保存爲一個html文件,就可以在瀏覽器中看到效果了:

<table border="1">
<tr><th>Name</th><th>Score</th><tr>
<tr><td>Lisa</td><td>85</td></tr>
<tr><td>Adam</td><td>95</td></tr>
<tr><td>Bart</td><td>59</td></tr>
</table>

實例:在生成的表格中,對於沒有及格的同學,請把分數標記爲紅色。

提示:紅色可以用 <td style="color:red"> 實現。


參考代碼:

d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
def generate_tr(name, score):
    if score < 60:
        return '<tr><td>%s</td><td style="color:red">%s</td></tr>' % (name, score)
    else:
        return '<tr><td>%s</td><td>%s</td></tr>' % (name, score)

tds = [generate_tr(name, score) for name, score in d.iteritems()]
print '<table border="1">'
print '<tr><th>Name</th><th>Score</th><tr>'
print '\n'.join(tds)
print '</table>'

3、條件過濾

      列表生成式的 for 循環後面還可以加上 if 判斷。例如:
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

如果我們只想要偶數的平方,不改動 range()的情況下,可以加上 if 來篩選:

>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

有了 if 條件,只有 if 判斷爲 True 的時候,才把循環的當前元素添加到列表中。

實例:編寫一個函數,它接受一個 list,然後把list中的所有字符串變成大寫後返回,非字符串元素將被忽略。

提示:

1. isinstance(x, str) 可以判斷變量 x 是否是字符串;

2. 字符串的 upper() 方法可以返回大寫的字母。

參考代碼:

def toUppers(L):
    return [x.upper() for x in L if type(x) == str]

print toUppers(['Hello', 'world', 101])

運行結果:

['HELLO', 'WORLD']

4、多層表達式

      for循環可以嵌套,因此,在列表生成式中,也可以用多層 for 循環來生成列表。

對於字符串 'ABC' 和 '123',可以使用兩層循環,生成全排列:

>>> [m + n for m in 'ABC' for n in '123']
['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']

翻譯成循環代碼就像下面這樣:

L = []
for m in 'ABC':
    for n in '123':
        L.append(m + n)

實例:利用 3 層for循環的列表生成式,找出對稱的 3 位數。例如,121 就是對稱數,因爲從右到左倒過來還是 121。

參考代碼:

print [x for x in range(100, 1000) if (x / 100 == x % 10)]

運行結果:

 151, 161, 171, 181, 191, 202, 212, 222, 232, 242, 252, 262, 272, 282, 292, 303, 313, 323, 333, 343, 353, 363, 373, 383, 393, 404, 414, 424, 434, 444, 454, 464, 474, 484, 494, 505, 515, 525, 535, 545, 555, 565, 575, 585, 595, 606, 616, 626, 636, 646, 656, 666, 676, 686, 696, 707, 717, 727, 737, 747, 757, 767, 777, 787, 797, 808, 818, 828, 838, 848, 858, 868, 878, 888, 898, 909, 919, 929, 939, 949, 959, 969, 979, 989, 999]

轉載請註明出處:

http://blog.csdn.net/daijin888888/article/details/79344334


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章