作者:萬澤耀
來自:http://yaoyaoblog.xyz/2017/09/04/wtfPython—Python中一些奇妙的程式碼/
wtfPython是github上的一個專案,作者收集了一些奇妙的Python程式碼片段,這些程式碼的輸出結果會和我們想象中的不太一樣;
透過探尋產生這種結果的內部原因,可以讓我們對Python裡的一些細節有更廣泛的認知。
1.字典鍵的隱式轉換
some_dict = {}
some_dict[5.5] = "Ruby"
some_dict[5.0] = "JavaScript"
some_dict[5] = "Python"
輸出如下:
>>> some_dict
{5.0: "Python", 5.5: "Ruby"}
>>> some_dict[5.5]
"Ruby"
>>> some_dict[5.0]
"Python"
>>> some_dict[5]
"Python"
原因:
Python的字典鍵的比較是透過雜湊值來比較的
在Python裡如果兩個不可變物件的值相等,那他們的雜湊也是一樣的
因此此處hash(5) == hash(5.0)是True的,所以鍵被隱式的轉換了
2.生成器執行時間的差異
array = [1, 8, 15]
g = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]
輸出:
>>> print(list(g))
[8]
原因
在一個生成器運算式裡,in的操作是在宣告時求值的,而if是在執行期求值的
所以在執行期之前,array已經被重新分配成了[2,8,22],x的值也是2,8,22
3.在串列迭代式刪除item
list_1 = [1, 2, 3, 4]
list_2 = [1, 2, 3, 4]
list_3 = [1, 2, 3, 4]
list_4 = [1, 2, 3, 4]
for idx, item in enumerate(list_1):
del item
for idx, item in enumerate(list_2):
list_2.remove(item)
for idx, item in enumerate(list_3[:]):
list_3.remove(item)
for idx, item in enumerate(list_4):
list_4.pop(idx)
輸出:
>>> list_1
[1, 2, 3, 4]
>>> list_2
[2, 4]
>>> list_3
[]
>>> list_4
[2, 4]
原因
其實只有list3才算是合格的寫法,對一個正在迭代的物件進行修改並不是一個很好的選擇,正確的做法應該是建立一份該物件的複製來進行迭代
對於list1,del item刪除的只是item變數而不是變數指向的資料,對串列本身沒有影響
對於list2和list4,因為串列的迭代是根據索引來的,第一次刪掉了索引為0的1,剩下[2, 3, 4],然後移除索引 1(此時為3),剩下了[2, 4],此時只有2個元素,迴圈結束
4.else的不同處理
對於迴圈的else
def does_exists_num(l, to_find):
for num in l:
if num == to_find:
print("Exists!")
break
else:
print("Does not exist")
輸出:
>>> some_list = [1, 2, 3, 4, 5]
>>> does_exists_num(some_list, 4)
Exists!
>>> does_exists_num(some_list, -1)
Does not exist
對於try的else
try:
pass
except:
print("Exception occurred!!!")
else:
print("Try block executed successfully...")
輸出:
Try block executed successfully...
原因
迴圈後的else只會在經過了所有迭代且沒有出現break的時候才會執行
一個try模組後的else會在try裡的程式碼成功執行完後去執行
5.python裡的is
>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False
原因
is和==是不一樣的;is判斷的是兩個物件是否是同一個物件,而==判斷的是兩個物件的值是否相等;即is是既要值相等又要取用一致
在Python中-5~256因為被經常使用所以被設計成固定存在的物件
6.迴圈裡的區域性變數洩露
程式碼段1
for x in range(7):
if x == 6:
print(x, ': for x inside loop')
print(x, ': x in global')
輸出:
6 : for x inside loop
6 : x in global
程式碼段2
# This time let's initialize x first
x = -1
for x in range(7):
if x == 6:
print(x, ': for x inside loop')
print(x, ': x in global')
輸出:
6 : for x inside loop6 : x in global
程式碼段3
x = 1
print([x for x in range(5)])
print(x, ': x in global')
在Python2.x裡的輸出:
[0, 1, 2, 3, 4](4, ': x in global')
在Python3.x裡的輸出:
[0, 1, 2, 3, 4]1 : x in global
原因
對於程式碼段1,在Python中,for迴圈可以使用包含他們的名稱空間的變數,並將他們自己定義的迴圈變數儲存下來;* 對於程式碼段2,如果我們在全域性名稱空間裡顯示定義for迴圈變數,則迴圈變數會重新系結到現有變數上。
對於程式碼段3,在Python3.x中改變了對串列解析的語法形式;Python2.x中,串列解析的語法形式為:[… for var in item1, item2, …];而Python3.x的串列解析式為:[… for var in (item1, item2, …)],這種情況下不會發生迴圈變數的洩露
7.+和+=的區別
程式碼段1
a = [1, 2, 3, 4]
b = a
a = a + [5, 6, 7, 8]
輸出:
>>> a
[1, 2, 3, 4, 5, 6, 7, 8]
>>> b
[1, 2, 3, 4]
程式碼段2
a = [1, 2, 3, 4]
b = a
a += [5, 6, 7, 8]
輸出:
>>> a
[1, 2, 3, 4, 5, 6, 7, 8]
>>> b
[1, 2, 3, 4, 5, 6, 7, 8]
原因
a = a + b的操作生成了一個新的物件並建立了一個新的取用
a += b是在a這個串列上做extend操作
8.關於try—finally裡的return
def some_func():
try:
return 'from_try'
finally:
return 'from_finally'
輸出:
>>> some_func()
'from_finally'
原因
在try…finally這種寫法裡面,finally中的return陳述句永遠是最後一個執行
一個函式的return的值是由最後一個return陳述句來決定的
9.True=False
True = False
if True == False:
print("I've lost faith in truth!")
輸出:
I've lost faith in truth!
原因
最開始的時候,Python是沒有bool型別的(使用0表示false,使用非0值表示真),後來加上了True,False和bool型別;但是為了向後相容性,True和False並沒有被設定成常量,而只是一個內建變數,所以可以被賦值修改
在Python3當中,因為並沒有向後相容,所以不會有這種情況發生
10.一步操作,從有到無
some_list = [1, 2, 3]
some_dict = {
"key_1": 1,
"key_2": 2,
"key_3": 3
}
some_list = some_list.append(4)
some_dict = some_dict.update({"key_4": 4})
輸出:
>>> print(some_list)
None
>>> print(some_dict)
None
原因
許多修改序列/對映物件的方法(例如list.append, dict.update, list.sort等等)都是直接修改物件並傳回一個None;所以平常碰到這種直接修改的操作,應該避免直接賦值。
11.Python的for
for i in range(4):
print(i)
i = 10
輸出:
0
1
2
3
原因
Python的for迴圈機制是每次迭代到下一項的時候都會解包並分配一次;即range(4)裡的四個值在每次迭代的時候都會解包一次並賦值;所以i = 10對迭代沒有影響。
●編號498,輸入編號直達本文
●輸入m獲取文章目錄
Web開發
更多推薦《18個技術類微信公眾號》
涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。