3. 循環

你知道了如何在條件爲真(或假)時執行操作,但如何重複操作多次呢?

簡單的例子,假設要打印1~100的所有數。你可採用笨辦法。
print(1)
print(2)
print(3)
...
print(99)
print(100)
但你願意使用笨辦法嗎?
循環語句流程結構:

在這裏插入圖片描述

1. while循環

#爲避免前述示例所示的繁瑣代碼,能夠像下面這樣做很有幫助:
#打印1~100之間的所有整數
x = 1
while x <= 100:
print(x)
x += 1
#還可以使用循環來確保用戶輸入名字,如下所示:
name = ''
while not name:
    name = input('Please enter your name: ')
print('Hello, {}!'.format(name))

2. for循環

'''
	while 語句非常靈活,可用於在條件爲真時反覆執行代碼塊。這在通常情況下很好,但有時候你可能想根據需要進行定製。
一種這樣的需求是爲序列(或其他可迭代對象)中每個元素執行代碼塊。
'''
#訪問序列元素方式一:
words = ['this', 'is', 'an', 'ex', 'parrot']
for word in words:
    print(word)
#訪問序列元素方式二:
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for number in numbers:
    print(number)

#鑑於迭代(也就是遍歷)特定範圍內的數是一種常見的任務,Python提供了一個創建範圍的內置函數。
>>> range(0, 10)
range(0, 10)
>>> list(range(0, 10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
'''
	範圍類似於切片。它們包含起始位置(這裏爲0),但不包含結束位置(這裏爲10)。在很多情況下,你都希望範圍的起始位置
爲0。實際上,如果只提供了一個位置,將把這個位置視爲結束位置,並假定起始位置爲0。
'''
>>> range(10)
range(0, 10)

#下面的程序打印數1~100:
for number in range(1,101):
	print(number)
#注意:相比前面使用的 while 循環,這些代碼要緊湊得多。

3. 迭代字典

#要遍歷字典的所有關鍵字,可像遍歷序列那樣使用普通的 for 語句。
d = {'x': 1, 'y': 2, 'z': 3}
for key in d:
    print(key, 'corresponds to', d[key])
'''
	也可使用 keys 等字典方法來獲取所有的鍵。如果只對值感興趣,可使用 d.values 。你可能還記得, d.items 以元組的方式
返回鍵值對。 for 循環的優點之一是,可在其中使用序列解包。
'''
for key, value in d.items():
	print(key, 'corresponds to', value)

4. 迭代工具

'''
1. 並行迭代
	同時迭代倆序列:name和age,如果要打印名字和對應的年齡,可以像下面這樣做:
'''
names = ['anne', 'beth', 'george', 'damon']
ages = [12, 45, 32, 102]
for i in range(len(names)):
	print(names[i], 'is', ages[i], 'years old')
'''
	i 是用作循環索引的變量的標準名稱。一個很有用的並行迭代工具是內置函數 zip ,它將兩個序列“縫合”起來,並返回一個由元組
組成的序列。返回值是一個適合迭代的對象,要查看其內容,可使用 list 將其轉換爲列表。
'''
>>> list(zip(names, ages))
[('anne', 12), ('beth', 45), ('george', 32), ('damon', 102)]
#“縫合”後,可在循環中將元組解包。
for name, age in zip(names, ages):
	print(name, 'is', age, 'years old')
#函數 zip 可用於“縫合”任意數量的序列。需要指出的是,當序列的長度不同時,函數 zip 將在最短的序列用完後停止“縫合”。
>>> list(zip(range(5), range(100000000)))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]


'''
2. 迭代時獲取索引
	在有些情況下,你需要在迭代對象序列的同時獲取當前對象的索引。例如,你可能想替換一個字符串列表中所有包含子串 'xxx' 
的字符串。當然,完成這種任務的方法有很多,但這裏假設你要像下面這樣做:
'''
for string in strings:
    if 'xxx' in string:
        index = strings.index(string) # 在字符串列表中查找字符串
        strings[index] = '[censored]'
'''
	這可行,但替換前的搜索好像沒有必要。另外,如果沒有替換,搜索返回的索引可能不對(即返回的是該字符串首次出現處的
索引)。下面是一種更佳的解決方案:
'''
index = 0
for string in strings:
    if 'xxx' in string:
        strings[index] = '[censored]'
    index += 1
#這個解決方案雖然可以接受,但看起來也有點笨拙。另一種解決方案是使用內置函數enumerate 。
#enumerate函數讓你能夠迭代索引值對,其中的索引是自動提供的。
for index, string in enumerate(strings):
    if 'xxx' in string:
        strings[index] = '[censored]'
'''
3. 反向迭代和排序後再迭代
	來看另外兩個很有用的函數: reversed 和 sorted 。它們類似於列表方法 reverse 和 sort(sorted接受的參數也與 
sort 類似),但可用於任何序列或可迭代的對象,且不就地修改對象,而是返回反轉和排序後的版本。
'''
>>> sorted([4, 3, 6, 8, 3])
[3, 3, 4, 6, 8]
>>> sorted('Hello, world!')
[' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
>>> list(reversed('Hello, world!'))
['!', 'd', 'l', 'r', 'o', 'w', ' ', ',', 'o', 'l', 'l', 'e', 'H']
>>> ''.join(reversed('Hello, world!'))
'!dlrow ,olleH'
'''
	請注意, sorted 返回一個列表,而 reversed 像 zip 那樣返回一個更神祕的可迭代對象。你無需關心這到底意味着什麼,
只管在 for 循環或 join 等方法中使用它,不會有任何問題。只是你不能對它執行索引或切片操作,也不能直接對它調用列表的
方法。要執行這些操作,可先使用 list 對返回的對象進行轉換。
'''

5. 跳出循環

	通常,循環會不斷地執行代碼塊,直到條件爲假或使用完序列中的所有元素。但在有些情況下,你可能想中斷循環、開始新迭代
(進入“下一輪”代碼塊執行流程)或直接結束循環。
'''
1. break
	要結束(跳出)循環,可使用 break 。假設你要找出小於100的最大平方值(整數與自己相乘的結果),可從100開始向下迭代。
找到一個平方值後,無需再迭代,因此直接跳出循環。
'''
from math import sqrt
for n in range(99, 0, -1):
    root = sqrt(n)
    if root == int(root):
        print(n)
        break
'''
	如果你運行這個程序,它將打印81並結束。注意到我向 range 傳遞了第三個參數——步長,即序列中相鄰數的差。通過將步長設
置爲負數,可讓 range 向下迭代,如上面的示例所示;還可讓它跳過一些數:
'''
>>> range(0, 10, 2)
[0, 2, 4, 6, 8]

'''
2. continue
	語句 continue 沒有 break 用得多。它結束當前迭代,並跳到下一次迭代開頭。這基本上意味着跳過循環體中餘下的語句,
但不結束循環。這在循環體龐大而複雜,且存在多個要跳過它的原因時很有用。在這種情況下,可使用 continue ,如下所示:
'''
for x in seq:
    if condition1: continue
    if condition2: continue
    if condition3: continue
    do_something()
    do_something_else()
    do_another_thing()
    etc()
# 然而,在很多情況下,使用一條 if 語句就足夠了。
for x in seq:
    if not(condition1 or condition2 or condition3):
        do_something()
        do_something_else()
        do_another_thing()
        etc()
#continue 雖然是一個很有用的工具,但並非不可或缺的。然而,你必須熟悉 break 語句,因爲在 while True 循環中經常用到。
'''
3. while True/break 成例
	在Python中, for 和 while 循環非常靈活,但偶爾遇到的一些問題可能讓你禁不住想:如果這些循環的功能更強些就好了。
例如,假設你要在用戶根據提示輸入單詞時執行某種操作,並在用戶沒有提供單詞時結束循環。爲此,一種辦法如下:
'''
while True:
    word = input('Please enter a word: ')
    if not word: break
    # 使用這個單詞做些事情:
    print('The word was ', word)
'''
6. 循環中的 else 子句
	通常,在循環中使用 break 是因爲你“發現”了什麼或“出現”了什麼情況。要在循環提前結束時採取某種措施很容易,但有時候
你可能想在循環正常結束時才採取某種措施。如何判斷循環是提前結束還是正常結束的呢?可在循環開始前定義一個布爾變量並將其
設置爲 False ,再在跳出循環時將其設置爲 True 。這樣就可在循環後面使用一條 if 語句來判斷循環是否是提前結束的。
'''
broke_out = False
for x in seq:
	do_something(x)
	if condition(x):
		broke_out = True
		break
	do_something_else(x)
if not broke_out:
	print("I didn't break out!")
# 一種更簡單的辦法是在循環中添加一條 else 子句,它僅在沒有調用 break 時才執行。繼續前面討論 break 時的示例。
from math import sqrt
for n in range(99, 81, -1):
    root = sqrt(n)
    if root == int(root):
        print(n)
        break
else:
    print("Didn't find it!")

6. pass 、 del 和 exec

'''
1. 什麼也不做
	什麼也不做,情況不多,但一旦遇到,知道可使用 pass 語句大有裨益。
'''
>>> pass
>>>
'''
這裏什麼都沒有發生。
	那麼爲何需要一條什麼都不做的語句呢?在你編寫代碼時,可將其用作佔位符。例如,你可能編寫了一條 if 語句並想嘗試
運行它,但其中缺少一個代碼塊,如下所示:
'''
if name == 'Ralph Auldus Melish':
    print('Welcome!')
elif name == 'Enid':
    # 還未完成……
elif name == 'Bill Gates':
    print('Access Denied')
#這些代碼不能運行,因爲在Python中代碼塊不能爲空。要修復這個問題,只需在中間的代碼塊中添加一條 pass 語句即可。
if name == 'Ralph Auldus Melish':
    print('Welcome!')
elif name == 'Enid':
    # 還未完成……
    pass
elif name == 'Bill Gates':
    print('Access Denied')

'''
2. 使用 del 刪除
對於你不再使用的對象,Python通常會將其刪除(因爲沒有任何變量或數據結構成員指向它)。
'''
>>> scoundrel = {'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin = scoundrel
>>> scoundrel
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> scoundrel = None
>>> robin
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin = None
'''
	最初, robin 和 scoundrel 指向同一個字典,因此將 None 賦給 scoundrel 後,依然可以通過 robin來訪問這個字典。
但將 robin 也設置爲 None 之後,這個字典就漂浮在計算機內存中,沒有任何名稱與之相關聯,再也無法獲取或使用它了。因此
,智慧無窮的Python解釋器直接將其刪除。這被稱爲垃圾收集。請注意,在前面的代碼中,也可將其他任何值(而不是 None )
賦給兩個變量,這樣字典也將消失。

	另一種辦法是使用 del 語句。(第2章和第4章使用這條語句來刪除序列和字典,還記得嗎?)這不僅會刪除到對象的引用,
還會刪除名稱本身。
'''
>>> x = 1
>>> del x
>>> x
Traceback (most recent call last):
File "<pyshell#255>", line 1, in ?
x
NameError: name 'x' is not defined

#這看似簡單,但有時不太好理解。例如,在下面的示例中, x 和 y 指向同一個列表:
>>> x = ["Hello", "world"]
>>> y = x
>>> y[1] = "Python"
>>> x
['Hello', 'Python']
#你可能認爲通過刪除 x ,也將刪除 y ,但情況並非如此。
>>> del x
>>> y
['Hello', 'Python']
#這是爲什麼呢? x 和 y 指向同一個列表,但刪除 x 對 y 沒有任何影響,因爲你只刪除名稱 x ,而沒有刪除列表本身(值)。
#事實上,在Python中,根本就沒有辦法刪除值,而且你也不需要這樣做,因爲對於你不再使用的值,Python解釋器會立即將其刪除。

'''
3. 使用 exec 和 eval 執行字符串及計算其結果
	有時候,你可能想動態地編寫Python代碼,並將其作爲語句進行執行或作爲表達式進行計算。這可能猶如黑暗魔法,一定要小心。
 exec 和 eval 現在都是函數,但 exec 以前是一種語句,而 eval與它緊密相關。這就是我在這裏討論它們的原因所在。

3.1 1. exec
	函數 exec 將字符串作爲代碼執行
'''
>>> exec("print('Hello, world!')")
Hello, world!
'''
	然而,調用函數 exec 時只給它提供一個參數絕非好事。在大多數情況下,還應向它傳遞一個命名空間——用於放置變量的地方;
否則代碼將污染你的命名空間,即修改你的變量。例如,假設代碼使用了名稱 sqrt ,結果將如何呢?
'''
>>> from math import sqrt
>>> exec("sqrt = 1")
>>> sqrt(4)
Traceback (most recent call last):
	File "<pyshell#18>", line 1, in ?
		sqrt(4)
TypeError: object is not callable: 1
'''
	既然如此,爲何要將字符串作爲代碼執行呢?函數 exec 主要用於動態地創建代碼字符串。如果這種字符串來自其他地方(可能
是用戶),就幾乎無法確定它將包含什麼內容。因此爲了安全起見,要提供一個字典以充當命名空間。
'''
#爲此,你添加第二個參數——字典,用作代碼字符串的命名空間
>>> from math import sqrt
>>> scope = {}
>>> exec('sqrt = 1', scope)
>>> sqrt(4)
2.0
>>> scope['sqrt']
1
'''
	如你所見,可能帶來破壞的代碼並非覆蓋函數 sqrt 。函數 sqrt 該怎樣還怎樣,而通過 exec 執行賦值語句創建的變量位於
 scope 中。請注意,如果你嘗試將 scope 打印出來,將發現它包含很多內容,這是因爲自動在其中添加了包含所有內置函數和值
 的字典 __builtins__ 。
'''
>>> len(scope)
2
>>> scope.keys()
['sqrt', '__builtins__']




'''
3.2 eval
	eval 是一個類似於 exec 的內置函數。 exec 執行一系列Python語句,而 eval 計算用字符串表示的Python表達式的值,並
返回結果( exec 什麼都不返回,因爲它本身是條語句)。例如,你可使用如下代碼來創建一個Python計算器:
'''
>>> eval(input("Enter an arithmetic expression: "))
Enter an arithmetic expression: 6 + 18 * 2
42
#與 exec 一樣,也可向 eval 提供一個命名空間,雖然表達式通常不會像語句那樣給變量重新賦值。
#向 exec 或 eval 提供命名空間時,可在使用這個命名空間前在其中添加一些值。
>>> scope = {}
>>> scope['x'] = 2
>>> scope['y'] = 3
>>> eval('x * y', scope)
6

#同樣,同一個命名空間可用於多次調用 exec 或 eval 。
>>> scope = {}
>>> exec('x = 2', scope)
>>> eval('x * x', scope)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章