史上最全Python學習筆記(基於《Python學習手冊(第4版)》)——Part3 語句和語法

Chap10 Python語句簡介

重訪Python程序結構

本章深入學習內容:

  1. 程序由模塊構成
  2. 模塊包含語句
  3. 語句包含表達式
  4. 表達式建立並處理對象

Python的語句

語句 角色 例子
賦值 創建引用之 a,b,c=‘good’,‘bad’,‘ugly’
調用 執行函數 log.write(“spam,ham”)
打印調用 打印對象 print(“The Killer”,joke)
if/elif/else 選擇動作 if “python” in text:
     print(text)
for/else 序列迭代 for x in mylist:
    print(x)
while/else 一般循環 while x>y:
    print(‘hello’)
pass 空佔位符 while True:
    pass
break 循環退出 while True: if exittest():
    break
continue 循環繼續 while True:
    if skiptest():
        continue
def 函數和方法 def f(a,b,c=1,*d):
    print(a+b+c+d[0])
return 函數結果 def f(a,b,c=1,*d):
    return a+b+c+d[0]
yield 生成器函數 def gen(n):
    for i in n:
        >yield i*2
global 命名空間 x='old’
def function():
    global x,y;x=‘new’
nonlocal 命名空間(Python3.0及以上版本) def outer():
    x=‘old’
    def function():
        nonlocal x;x=‘new’
import 模塊訪問 import sys
from 屬性訪問 from sys import stdin
class 創建對象 class Subclass(Superclass):
    staticData=[]
    def method(self):pass
try/except/finally 捕捉異常 try:
    action()
except:
    print(‘action error’)
raise 觸發異常 raise EndSearch(location)
assert 調試檢查 assert X>Y,‘X too small’
with/as 環境管理器(2.6) with open(‘data’)as myfile:
    process(myfile)
del 刪除引用 del data[k]
del data[i:j]
del obj.attr
del variable

對以上內容的一些說明:

  • 賦值語句以不同的語法形式呈現,有如針對基本的、序列的、擴展的等待
  • 從技術上來講,print在Python3.0中不是一個保留字,也不是一條語句,而是一個內置函數的調用
  • yield實際上是一個表達式而不是一條語句

兩個if的故事

Python中的if語句比其他語言的更簡潔,條件判讀不加圓括號,語句塊不加花括號。

Python增加了什麼

Python中新的語法成分是冒號(?。所有Python的複合語句(也就是語句中嵌套了語句)都有相同的一般形式,也就是首行以冒號結尾,首行下一行嵌套的代碼往往按縮進的格式書寫。

冒號是不可或缺的,遺漏掉冒號可能是Python新孚最常犯的錯誤之一 。

Python刪除了什麼

雖然Python需要額外的冒號,但是你必須在類C語言程序中加入,而通常不需要在 Python中加入的語法成分卻有三項。

括號是可選的

Python中省略括號而語句依然會正常執行。

終止行就是終止語句

不會出現在Python程序代碼中的第二個重要的語也成分就是分號。 Pytbon之中你不需要 像類C語言那樣用分號終止語句。

縮進的結束就是代碼塊的結束

Chap11 賦值、表達式和打印

賦值語句

賦值語句很簡單,但有些特性需要專門記住:

  • 賦值語句建立對象引用值。Python賦值語句會把對象引用值存儲在變量名或數據結構的元素內。賦值語句總是建立對象的引用值,而不是複製對象。
  • 變量名在首次賦值時會被創建
  • 變量名在引用前必須先賦值。使用尚未進行賦值的變量名時一種錯誤。
  • 執行隱式賦值的一些操作。模塊導入、函數和類的定義、for循環變量以及函數參數全都是隱式賦值運算。

賦值語句的形式

運算 解釋
spam=‘Spam’ 基本形式
spam,ham=‘yum’,‘YUM’ 元組賦值運算(位置性)
[spam,ham]=[‘yum’,‘YUM’] 列表賦值運算(位置性)
a,b,c,d=‘spam’ 序列賦值運算,通用性
a,*b=‘spam’ 擴展的序列解包(Python3.0)
spam=ham=‘lunch’ 多目標賦值運算
spams+=42 增強賦值運算

序列賦值

高級序列賦值語句模式

# 使用分片賦值
string='SPAM'
a,b,c=string[0],string[1],string[3]
a,b,c
('S', 'P', 'M')
a,b,c=list(string[:2])+[string[2:]]
a,b,c
('S', 'P', 'AM')
((a,b),c)=('SP','AM')
a,b,c
('S', 'P', 'AM')
#序列解包賦值
red,green,blue=range(3)
red,blue
(0, 2)
# 循環把序列分割爲開頭和剩餘的兩部分
L=[1,2,3,4]
while L:
    front,L=L[0],L[1:]
    print(front,L)

1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []
# 上例的另一種實現
L=[1,2,3,4]
while L:
    front,L=L.pop(0),L
    print(front,L)

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

Python3.0中的擴展序列解包

實際應用

在Python3.0中,可以在目標中使用*來更通用地匹配,這種方法等價於分片賦值,但更方便和簡單:

seq=[1,2,3]
a,*b=seq
a,b
(1, [2, 3])
*a,b=seq
a,b
([1, 2], 3)
seq='spam'
a,*b,c=seq
a,b,c
('s', ['p', 'a'], 'm')

邊界情況

一些值得注意的點:

  • 帶星號的名稱可能只匹配單個的項,但仍會向其賦值一個列表
  • 如果沒有剩下的內容可以匹配帶星號的名稱,則會被賦值爲一個空的列表,不管它出現在哪個位置
  • 如果由多個帶星號的名稱,或者如果值少了而沒有帶星號的名稱,以及如果帶星號的名稱自身沒有編寫到一個列表中,都會引發錯誤
seq=[1,2,3,4]
a,b,c,*d=seq
a,b,c,d
(1, 2, 3, [4])
a,b,c,d,*e=seq
a,b,c,d,e
(1, 2, 3, 4, [])
a,b,*c,d,e=seq
a,b,c,d,e
(1, 2, [], 3, 4)
a,*b,c,*d=seq
a,b,c,d
  File "<ipython-input-16-13bccea04c44>", line 1
    a,*b,c,*d=seq
                 ^
SyntaxError: two starred expressions in assignment
a,b=seq
a,b
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-17-0fc500ff2a50> in <module>()
----> 1 a,b=seq
      2 a,b


ValueError: too many values to unpack (expected 2)
*a=seq
a
  File "<ipython-input-18-561b7861cc41>", line 1
    *a=seq
          ^
SyntaxError: starred assignment target must be in a list or tuple
*a,=seq
a
[1, 2, 3, 4]

應用於for循環

for(a,*b,c)in [(1,2,3,4),(5,6,7,8)]:
    print(a,b,c)
1 [2, 3] 4
5 [6, 7] 8

多目標賦值語句

多目標賦值語句就是直接把所有提供的變量名都賦值給右側的對象。

多目標賦值以及共享引用

對於不可在原處修改的對象:更改其中一個變量名不會影響其他變量;

對於可以在原處修改的對象:在原處更改其中一個變量名會影響其他變量。

a=b=c=1
print(a,b,c)
b=b+1
print(a,b,c)
1 1 1
1 2 1
a=b=c=[]
print(a,b,c)
b.append(23)
print(a,b,c)
[] [] []
[23] [23] [23]
a=b=c=[1,2,3]
print(a,b,c)
b.pop()
print(a,b,c)
[1, 2, 3] [1, 2, 3] [1, 2, 3]
[1, 2] [1, 2] [1, 2]
# 可在原處更改的對象,重新對其中一個變量賦值,不會影響其他對象
a=b=c=[1,2,3]
print(a,b,c)
b='string'
print(a,b,c)
[1, 2, 3] [1, 2, 3] [1, 2, 3]
[1, 2, 3] string [1, 2, 3]

增強的賦值語句

諸如+=、*=之類的賦值語句爲增強的賦值語句

增強賦值以及共享引用

“+=”對列表和字典這類可變對象其實是在原處修改,並不像“+”合併,總是生成新的對象。當然對於不可變對象則不存在這種問題

L=[1,2]
M=L
L=L+[3,4]
L,M
([1, 2, 3, 4], [1, 2])
L=[1,2]
M=L
L+=[3,4]
L,M
([1, 2, 3, 4], [1, 2, 3, 4])

變量命名規則

Python變量命名需要遵循以下規則:

語法:(下劃線或字母)+(任意數目的字母、數字或下劃線)

區分大小寫:SPAM和spam並不同

禁止使用保留字

保留字

Python3.0中的保留字:

False class finally is return

None continue for lambda try

True def from nonlocal while

and del global not with

as elif if or yield

assert else import pass

break except in raise

命名慣例

  • 以單一下劃線開頭的變量名(_X)不會被from module import *語句導入
  • 前後有下劃線的變量名(__X__)是系統定義的變量名,對解釋器有特殊意義
  • 以兩個下劃線開頭、但結尾沒有兩個下劃線的變量名(__X)類的本地(“壓縮”)變量
  • 通過交互模式運行時,只有單個下劃線的變量名(_)會保存最後表達式的結果
  • 類的變量名通常以大寫字母開頭,而模塊的變量名通常以小寫字母開頭

變量名沒有類型,但對象有

變量有類型,並且可能是可變的或不可變的。變量名知識對象的引用值。沒有不可變的觀念,也沒有相關聯的類型信息,除了它們在特定時刻碰巧所引用的對象的類型。

表達式語句

原處修改的一個常見的錯誤

表達式語句通常用於執行可於遠處修改列表的列表方法,然而初學者常常把這種運算寫成賦值語句:

L=[1,2]
L.append(3)
L
[1, 2, 3]
L=L.append(4)
print(L)
None

對於像列表調用append、sort或reverse這類在原處的修改的運算,一定是對列表做原處的修改,但這些方法在列表修改後並不會把列表返回。事實上,它們返回的是None對象(可以理解成無返回值)。因此如果賦值這類運算的結果給該變量的變量名,只會丟失列表(gc)。

打印

打印流重定向

print(x,y)等價於:
import sys

sys.stdout.write(str(x)+’ ‘+str(y)+’\n’)

重定向使用格式一:

import sys

sys.stdout=open(‘log.txt’,‘a’) # redirects prints to a file



print(x,y) # shows up in log.txt

重定向使用格式二:(更一般的形式)

print(x,y,file=open(‘log.txt’,‘a’))

Chap12 if測試和語法規則

if語句

通用格式

if test1:

 statements1

elif test2:

 statements2

else:

 statements3

真值測試

Python中:

  • 任何非零數字或者非空對象都爲真
  • 數字零、空對象以及特殊對象None都被認作是假
  • 比較和相等測試會遞歸地應用在數據結構中
  • 比較和相等測試會返回True和False
  • 布爾and和or運算符會返回真或假的操作對象

Python中三種布爾表達式運算符:

X and Y → 如果X和Y都是真,則爲真,X爲假不判斷Y;

X or Y → 如果X或Y爲真,則爲真,X爲真不判斷Y;

not X → 如果X爲假,則表達式爲真

2<3,3<2
(True, False)
2 or 3,3 or 2,[] or 2,{} or []
(2, 3, 2, [])
2 and 3,3 and 2,{} and []
(3, 2, {})

if/else三元表達式

格式爲

if x:

 a=y

else:

 a=z

等價於

a=y if x else z

更早的版本中則使用

a=((x and y) or z)

替代三元表達式

在Python中也可以使用如下表達式從列表中挑選真假值:

a=[z,y][bool(x)]  (實際就是用布爾值構建下標)

['f','t'][bool('')]
'f'
['f','t'][bool('spam')]
't'

更特殊的一些用法

# 返回一組對象中第一個非空的那個,否則返回None
# 格式爲 X=A or B or C or ... or None
X='' or [] or 'spam' or {'a':1} or None
print(X)

# 常用在爲某個變量指定一個默認值(如果未被賦值的話)
# X=A or default
A=''
B='spam'
def  mydefault(x):
    my=x or 'my'
    print(my)
mydefault(A)
mydefault(B)
spam
my
spam
# 布爾運算符表達式若用來調用函數執行實質或重要的工作時,如果短路規則生效,則附加效果不會生效
# if f1() or f2():...
# 爲了確保多個函數都會執行,應該在使用布爾表達式前先調用一遍
# tmp1,tmp2=f1(),f2()
# if tmp1 or tmp2:...

Chap13 while和for循環

while循環

一般格式

while test:

 statements1

else:

 statements2

break,continue,pass和循環else

在python中:

break→跳出最近所轄的循環(跳過整個循環語句)

continue→跳到最近雖遭循環的開頭處(來到循環的首行)

pass→什麼是也不做,只是空佔位語句

循環else塊→只有當循環正常離開時才執行(也就是沒有碰到break語句)

一般循環格式

加入break和continue語句後,while循環的一般格式如下:

while test1:

 statements1
 if test2:break

 if test3:continue

else:

 statements2

for循環

for循環在Python中是一個通用的序列迭代器:可以遍歷任何有序的序列對象內的元素。for語句可以用於字符串、列表、元組、其他內置可迭代對象以及之後能夠通過類所創建的新的對象。

一般格式

for target in object:

 statements1

else:

 statements2

例子

簡單的例子

# 一個元組迭代的簡單例子
T=((1,2),(3,4),(5,6))
for a,b in T:
    print(a,b)

1 2
3 4
5 6
# 一個字典迭代的簡單例子
D={'a':1,'b':2,'c':3}
for (key,value) in D.items() :
    print(key,'=>',value)

a => 1
b => 2
c => 3

Python3.0在for循環中擴展的序列賦值

在Python3.0中,由於一個序列可以賦值給一組更爲通用的名稱(其中有一個帶有星號的名稱收集多個元素),我們可以在for循環中使用同樣的方法來提取嵌套的序列的部分:

L=[(1,2,3,4),(5,6,7,8)]
for (a,*b,c) in L:
    print(a,b,c)
1 [2, 3] 4
5 [6, 7] 8

嵌套for循環

items=['aaa',111,(4,5),2.01]
tests=[(4,5),3.14]
for key in tests:
    for item in items:
        if item == key:
            print(key,"was found!")
            break
    else:
        print(key,'not found!')

(4, 5) was found!
3.14 not found!
# in 能夠隱式地進行循環掃描列表尋找匹配項,因此在if中使用in來判斷而取代內嵌循環,效率會更高,程序也更簡潔
for key in tests:
    if key in items:
        print(key,"was found!")
    else:
        print(key,"not found!")

(4, 5) was found!
3.14 not found!

利用循環讀取文件

# 首先構建一個文件並添加內容
file=open('test.txt','w+')
L=['Hello World!\n','It is so nice to meet you!\n',
  'I need to go.\n','See you later!']
file.writelines(L)
file.close()
#while 循環逐行讀取
file=open('test.txt','r')
while True:
    line=file.readline()
    if not line:break
    print(line,end='')
file.close()
Hello World!
It is so nice to meet you!
I need to go.
See you later!
# for 循環逐行讀取
file=open('test.txt','r')
for line in file.readlines():
    print(line,end='')
file.close()
Hello World!
It is so nice to meet you!
I need to go.
See you later!

編寫循環的技巧

for循環博愛闊多數計數器式的循環。一般而言,for比while容易寫,執行時也比較快。

但是,有些情況下,需要以更爲特定的方式來進行迭代。python提供了兩個內置函數,以在for循環內定製迭代:

  • 內置range函數返回一系列連續增加的整數,可作爲for中的所有;
  • 內置zip函數返回並行元素的元組的列表,可用於在for中遍歷數個序列。
L=[(1,2,3),'spam',3.14]
for x in range(len(L)):
    print('L[',x,'] is',L[x])
L[ 0 ] is (1, 2, 3)
L[ 1 ] is spam
L[ 2 ] is 3.14

注意點:除非有特殊需求,否則最好儘可能使用簡單的for循環,不要使用while,並且不要在for中使用range調用,而是隻將其視爲最後的手段。

使用range的唯一真正有點是——它不會複製字符串,並且不會在Python3.0中創建一個列表,這對於很大的字符串來說,會很節省內存。

range常用的場合

# 指定步進切分字符串
string='abcdefghijklmn'
for x in range(0,len(string),2):
    print(string[x:x+2],end=',')
ab,cd,ef,gh,ij,kl,mn,
# 修改列表
L=[1,2,3,4,5]
for i in range(len(L)):
    L[i]+=1
L
[2, 3, 4, 5, 6]
# 實際上可以用列表解析來替代上例,且更高效
L=[1,2,3,4,5]
L=[x+1 for x in L]
L
[2, 3, 4, 5, 6]

並行遍歷:zip和map

range允許我們以非完備的方式在for循環捏完成遍歷。本着同樣的精神,內置的zip函數也可以讓我們使用for循環來並行使用多個序列。

# 假設有多個列表
L1=[1,2,3]
L2=[4,5,6]
L3=[7,8,9]
# 使用自拍創建一個元組以合併這些列表元素
Lall=list(zip(L1,L2,L3))
print(Lall)
# 使用for循環並行迭代
for(x,y,z) in zip(L1,L2,L3):
    print(x,y,z,'--',x+y+z)

[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
1 4 7 -- 12
2 5 8 -- 15
3 6 9 -- 18
# 擴展,使用並行迭代輸出多個列表
l1=[]
l2=[]
l3=[]
for (*x,) in zip(L1,L2,L3):
    l1.append(x.pop(0))
    l2.append(x.pop(0))
    l3.append(x.pop(0))
print(l1,l2,l3)
# 思考如何擴展以接受無上限的列表並輸出
[1, 2, 3] [4, 5, 6] [7, 8, 9]

zip的一些注意點:

  • zip可以接受任何類型的序列(即任何可迭代的對象,包括文件),並且可以有兩個以上的參數
  • 當參數長度不同是,zip會以最短序列的長度爲準來截斷所得到的元組

使用zip構造字典

keys=['spam','eggs','toast']
values=[1,3,5]
D={}
for (k,v) in zip(keys,values):
    D[k]=v
D
{'spam': 1, 'eggs': 3, 'toast': 5}

產生偏移和元素:enumerate

可以通過enumerate來產生偏移和元素,以替代range方法:

S='spam'
for(offset,item) in enumerate(S):
    print(item,'appears at offset',offset)

s appears at offset 0
p appears at offset 1
a appears at offset 2
m appears at offset 3

Chap14 迭代器和解析,第一部分

迭代器:初探

for循環可以用於Python中任何列類型,包括列表、元組以及字符串。但實際上,for循環甚至比這更爲通用:可用於任何可迭代的對象。其實對Python中所有會從左到右掃描對象的迭代工具而言都是如此,這些迭代工具包括了for循環、列表解析、in成員關係測試等。

迭代的實現機制

Python中的迭代協議:有__next__方法的對象會前進到下一個結果,而在一系列結果的末尾時,則會引發StopIteration異常。在Python中,任何這類對象都是可迭代的。任何這類對象也能以for循環或其他迭代工具遍歷,因此所有迭代工具內部工作來都是在每次迭代中調用__next__,並且捕捉StopIteration異常來確定何時離開。

手動迭代:iter和next

Python3.0提供了一個內置函數next,它會自動調用一個對象的__next__方法。給定一個可迭代對象,調用次方法等同於調用了__next__方法。

從技術的角度來講,迭代協議還有一點值得注意。當for循環開始時,會通過它傳給iter內置函數,以便從可迭代對象中獲得一個迭代器,返回的對象含有需要的next方法。

但是對文件來說,可以直接使用next方法而不要通過內置的iter函數來產生一個迭代器,因爲文件對象就是自己的迭代器。但是列表以及很多其他的內置對象,並不是自己的迭代器,因爲它們支持對此打開迭代器。對這樣的對象,必須調用iter來啓動迭代。

其它內置類型迭代器

字典的迭代器有:keys方法,values方法,items方法,以及其自身(返回的其實是keys)

迭代協議也是我們必須要把某些結果包裝到一個list調用中以一次性卡機難道它們的值的原因。因爲可迭代的對象一次返回一個結果,而不是實際的列表。

列表解析:初探

列表解析基礎知識

  • 列表解析寫在一個方括號裏,因爲它們最終是構建一個新的列表的方式。
  • 解析表達式以一個循環變量開始,後邊跟着應該看作是一個for循環的頭部部分,它聲明瞭循環變量以及一個可迭代對象
  • 從技術上講,列表解析並非真的是必須的,因爲總是可以用一個for循環手動地構建一個表達式結果的列表
  • 列表解析編寫起來更加精簡,且可以普遍使用於多種環境,此外列表解析比for循環運行更快(往往速度會快一倍),對於較大的數據集來說,使用列表解析更優

在文件上使用列表解析

f=open('test.txt')
lines=[line.rstrip() for line in f]
lines
['Hello World!',
 'It is so nice to meet you!',
 'I need to go.',
 'See you later!']
# 由於列表解析項for循環語句一樣是一個迭代環境,甚至可以不必提前打開文件,而是在表達式中打開,此時列表解析將自動使用迭代協議
lines=[line.rstrip() for line in open('test.txt')]
lines
['Hello World!',
 'It is so nice to meet you!',
 'I need to go.',
 'See you later!']

擴展的列表解析語法

作爲一個特別的擴展,表達式中嵌套的for循環可以有一個相關的if子句,來過濾那些測試部位真的結果項。

lines=[line.rstrip() for line in open('test.txt') if line[0]=='I']
lines
['It is so nice to meet you!', 'I need to go.']

如果需要的話,列表解析可以變得更復雜:例如它可能包含嵌套的循環,也可能被編寫爲一系列的for子句。實際上,列表解析的完整語法允許任意數目的for子句,每個子句有一個可選的相關的if子句(在20章會更加系統地介紹其語法)

[x+y for x in 'abc' for y in 'lmn']
['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']
# 快速輸出乘法表
# 注意和上例的循環嵌套順序的區別
print('\n'.join([' '.join(['%d*%d=%d'%(x,y,x*y) for x in range(1,y+1)])for y in range(1,10)]))
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
import time
L=[('吳俊傑','68'),('胡霞','98'),('陳立錦','64'),('陳琳','68'),('陳勇浩','73'),('陳仲珍','95'),('方怡彬','74'),('郭琦峯','76'),('郭如夢','82'),('黃航靈','98'),('黃詩鈴','97'),('黃淑嬋','71'),('黃雯婧','97'),('黃雨婷','61'),('黃長濤','98'),('江海明','68'),('李泓臻','65'),('李靜','69'),('林曉萍','65'),('劉鳳媚','81'),('羅曦','74'),('寧婉同','69'),('潘其威','60'),('戚鑑波','91'),('喬禹然','64'),('沙立','75'),('蘇汝正','92'),('唐丹丹','83'),('滕芷嫺','94'),('王彩','98'),('巫曉悅','91'),('吳瑜玲','93'),('閆夢瑤','79'),('楊雅婷','79'),('葉舟波','76'),('張彪','100'),('張麗亞','93'),('張琴心','67'),('張雨欣','86'),('莊子崧','70'),('鄒詠琪','99'),('蔡巧真','77'),('陳文穎','96'),('陳羲','91'),('董晉業','63'),('高鈺博','88'),('郭子瑋','88'),('何葉靈','100'),('黃丹泓','71'),('江鈺哲','62'),('姜子建','88'),('賴佳燕','91'),('李傑','81'),('李語萱','91'),('林瑞涵','74'),('林婷','82'),('林夏欣','95'),('林穎茹','79'),('劉瑋','86'),('劉雨倩','89'),('劉至禮','75'),('盧蓓婕','82'),('孫帥','63'),('文正霞','80'),('吳伊凡','72'),('肖躍鵬','64'),('徐福頓','62'),('徐文鑫','83'),('徐曉璇','95'),('許慧卿','63'),('許焱鑫','73'),('姚瑤','85'),('俞珺','85'),('餘悅','97'),('張靈涵','92'),('鄭可立','61'),('鍾靈','63'),('莊毓鴻','88'),('卓文浩','75'),('陳鴻宇','60'),('陳泓釗','78'),('陳景朝','97'),('陳俐琳','95'),('陳仁勝','61'),('陳少安','77'),('董鍇','72'),('付雅媛','73'),('黃琳','65'),('黃僞龍','97'),('賴心如','79'),('蘭子薇','73'),('李金星','93'),('李世平','73'),('梁偉傑','86'),('林雯琦','61'),('林宣宇','75'),('林志航','85'),('毛鑫平','94'),('彭陽','81'),('肖佳慧','79'),('謝國輝','91'),('楊彪','73'),('張家宸','83'),('張俊毅','92'),('張龍輝','67'),('張銘根','81'),('張宇斌','98'),('卓振烽','84'),('鄒仕星','92'),('蘇桂平','70')]

t1=time.time()
scores=[eval(s) for n,s in L]
tot_score=0
for x in scores:
    tot_score+=x
ave_score=tot_score/len(L)
#print("平均分:%.2f"%(ave_score)))
t2=time.time()
print(t2-t1)
0.0009961128234863281
from functools import reduce
t3=time.time()
scores=[eval(L[row][1]) for row in range(len(L))]
tot_score_score=reduce((lambda x,y:x+y),scores)
ave_score=tot_score/len(L)
#print('平均分:%.2f'%(ave_score))
t4=time.time()
print(t4-t3)
0.0009968280792236328

如今的Python中,迭代西醫甚至比目前所能展示的示例更爲普遍——Python的內置工具集中從左到右地掃描一個對象的每項工具,都定義爲在主體對象上使用了迭代協議。者甚至包含了更高級的工具,例如list和tuple內置函數(它們從可迭代杜希昂構建了一個新的對象),字符串join(將一個子字符串放置到一個可迭代對象中包含的字符串之間),甚至包括序列複製。總之,所有這些都將在一個打開的文件上工作並自動一次讀取一行:

list(open('test.txt'))
['Hello World!\n',
 'It is so nice to meet you!\n',
 'I need to go.\n',
 'See you later!']
tuple(open('test.txt'))
('Hello World!\n',
 'It is so nice to meet you!\n',
 'I need to go.\n',
 'See you later!')
'&&'.join((open('test.txt')))
'Hello World!\n&&It is so nice to meet you!\n&&I need to go.\n&&See you later!'

集合解析和字典解析

和列表解析一樣,集合解析和字典解析都支持列表解析的擴展語法,包括if測試。

Python3.0中的新的可迭代對象

Python3.0中的一個基本的改變是,它比Python2.X更強調迭代。除了於文件和字典這樣的內置類型相關的迭代,字典方法keys、values、和items都在Python3.0中返回可迭代對象,就像內置函數range、map、zip和filter所作的那樣。所有這些工具在Python3.0中都根據請求產生結果,而不像在Python2.6中那樣構建結果列表。

把各種函數和方法調用的結果都包含到一個list調用中,從而迫使它們一次產生其所有的結果,因爲Python3.0下上述工具不會直接返回列表而是返回一個可迭代對象。

多個迭代器VS單個迭代器

range內置函數支持len和索引,但它不是自己的迭代器,並且,它支持在其結果上的多個迭代器,這些迭代器會記住它們各自的位置:

R=range(3)
next(R)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-61-6acaa0ab8939> in <module>()
      1 R=range(3)
----> 2 next(R)


TypeError: 'range' object is not an iterator
I1=iter(R)
print(next(I1))
print(next(I1))
0
1
I2=iter(R)
print(next(I2))
0

相反,zip、map和filter不支持相同結果上的多個活躍迭代器:

Z=zip((1,2,3),(4,5,6))
I1=iter(Z)
I2=iter(Z)
print(next(I1))
print(next(I2))
print(next(I1))
(1, 4)
(2, 5)
(3, 6)
M=map(abs,(-1,0,1))
I1=iter(M)
I2=iter(M)
print(next(I1),next(I2),next(I1))
1 0 1

本部分相關習題

第一題

編寫基本循環

a.寫個for循環,打印字符串S中每個字符的ASCII碼。使用內置函數ord(char)把每個字符轉換成ASCII整數

b.接着,修改循環來計算字符串中所有字符的ASCII碼的總和

c.最後,再次修改代碼,來返回一個新的列表,其中包含字符串中每個字符的ASCII碼。表達式map(ord,S)是否有類似的效果?

S='this is my python'
for c in S:
    print(ord(c),end=' ')
116 104 105 115 32 105 115 32 109 121 32 112 121 116 104 111 110 
flag=0
for c in S:
    flag+=ord(c)
print(flag)
1660
L1=[ord(c) for c in S]
print(L1)

L2=list(map(ord,S))
print(L2)
[116, 104, 105, 115, 32, 105, 115, 32, 109, 121, 32, 112, 121, 116, 104, 111, 110]
[116, 104, 105, 115, 32, 105, 115, 32, 109, 121, 32, 112, 121, 116, 104, 111, 110]

第二題

排序字典。我們知道,字典是無序的集合體。編寫一個for循環來按照排序後(遞增)順序打印字典的項目。提示:使用字典keys和列表sort方法,或者較新的sorted內置函數。

D={'milk':10,'apple':20,'banana':15,'egg':40}
K=sorted(list(D.keys()))
for k in K:
    print(k,'=>',D[k])
apple => 20
banana => 15
egg => 40
milk => 10
# list.sort()返回None,因此不能使用上述方法,即不能直接賦值
K=list(D.keys())
K.sort()
for k in K:
    print(k,'=>',D[k])
apple => 20
banana => 15
egg => 40
milk => 10

第三題

程序邏輯替代方案。考慮下列代碼,是哦那個while循環以及found標誌位來搜索2的冪值列表[到2的5次方].它保存在名爲power.py的模塊文件中。

L=[1,2,8,16,32,64]
X=5
found=False
i=0
while not found and i < len(L):
    if 2** X==L[i]:
        found=True
    else:
        i=i+1
    if found:
        print('at index',i)
    else:
        print(X,'not found')

C:\book\tests>python power.py

at index 5

這個例子並沒有遵循一般的Python代碼編寫的技巧。遵循這裏所提到的步驟來改進它:

a. 首先,以while循環else分句重寫這個代碼來消除found標誌位和最終的if語句

b. 接着,使用for循環和else分句重寫這個例子,去掉列表索引運算邏輯。提示:要取得元素的索引,可以使用列表index方法(該方法返回列表中第一個元素的偏移量)

c. 接着,重寫這個例子,改用簡單的in運算符成員關係表達式,從而完全移除循環

d. 最後,使用for循環和列表append方法來產生列表,而不是通過列表常量硬編碼

深入思考:

e. 把2**x表達式移到循環外,這樣能夠改善性能嗎?如何編寫代碼?

f. 就像我們在練習1中看到過的,Python有一個map(function,list)工具也可以產生2次方的列表:map(lambda x:2 **x,range(7))。試着在交互模式下輸入這段代碼。

# 因爲in隱式地運用了循環,因此上例完全可以不用while或者for循環語句
L=[2**x for x in range(7) ]
X=5
if 2**X in L:
    print('at index',L.index(2**X))
else:
    print(X,'not found')

at index 5
list(map(lambda x:2**x,range(7)))
[1, 2, 4, 8, 16, 32, 64]
# 用題目中d步驟來編寫L列表
L=[]
for i in range(7):
    L.append(2**i)
L
[1, 2, 4, 8, 16, 32, 64]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章