歡迎光臨
每天分享高質量文章

wtfPython—Python中一些奇妙的程式碼

作者:萬澤耀
來自: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、資料庫、運維等。

贊(0)

分享創造快樂