Python陷阱彙總

2021-06-25 11:36:12 字數 4381 閱讀 5122

python雖然沒有c/c++的指標和各種資料型別轉換,但不代表它沒有一片坦途,對於初學者,再感嘆python的簡單和強大之時,可能一不小心就掉到陷阱中去了。為了給後來者警示,特總結python的各種陷阱,以起到「前車之覆,後車之鑑」的效果。

對於python函式的預設引數,它只在定義之時執行一次。對於不可變資料型別,如string型別等,可能沒什麼影響。但對於 list, dictionary,大多數類的例項物件等而言,區別就很大了。如下所示: 

def func1(a, b=):

print b

func1(1)

func1(2)

func1(3)

class a(object):

def __init__(self):

print("init a")

def func2(a, b=a()):

print(a)

func2(10)

func2(20)

func2(30)

輸出結果:

[1][1, 2]

[1, 2, 3]

init a

1020

30python函式的預設引數表示式,是在函式定義點,在其定義範圍內求值的,而不是在函式呼叫之時求值。**如下所示:

index = 100

def func(a = index):

print("a = %d"%(a))

index = 200

func()

輸出結果:

a = 100

在python迴圈體中,如果動態修改迴圈集合的大小(比如增加或刪除列表、字典的元素),會造成不可預料的結果。如下所示

>>> a = [3, 2, 2, 4]

>>> for i in a:

if i % 2 == 0:

a.remove(i)

>>> a

[3, 2]

一般會認為輸出結果為 [3],其實在刪除第二個元素「2」時,內部迭代器指示到了1位置,刪除「2」後,進入下次迴圈,迭代器增加1,指示到2(該位置的元素此時已經變為4了),導致第2個「2」被跳過了處理(此時「2」的位置為1)。

為了避免上述問題,需要保證在迴圈體內,不要動態修改迴圈集合長度,可以做份複製,或者利用列表的切片(slice)做複製,該方法很簡潔實用,但僅限於列表的迴圈。

**如下所示,從結果看出,已經達到我們預期目的了。

>>> a = [3, 2, 2, 4]

>>> for i in a[:]:

if i % 2 == 0:

a.remove(i)

>>> a

[3]>>>

我們都知道,python的string、tuple等型別都是不可變的(immutable)。但此處的不可變,是指它們的元素不會變(即容器裡的元素id是固定的),但元素的值是可變的(前提是該元素型別是可變的)。如下所示:

>>> a = [1, 2]

>>> b = ["hello", "world"]

>>> id(a)

19999120

>>> id(b)

19998120

>>> t = (a, b)

>>> t

([1, 2], ['hello', 'world'])

>>> id(t[0])

19999120

>>> id(t[1])

19998120

>>>

>>> t

([1, 2, 3], ['hello', 'world', '!!!'])

>>> id(t[0])

19999120

>>> id(t[1])

19998120

>>>

其中 t 是元組,為不可變型別,但其中的元素 a 和 b 均為列表,是可變的。如果我們改變了 a 和 b 的值,相應地, t 所包含的元素的值也改變了。 t 唯一不變的是,它所包含的元素還是原來的那個 a 和 b 元素(它們的id還是一樣的)。

complex([

real

[, imag]]

) 函式允許傳入形如「real+imagj」形式的字串,但不允許「+」號兩邊含有空格,這個比較坑人啊。如下所示:

>>> complex("1+3j")

(1+3j)

>>> complex("1 + 3j")

traceback (most recent call last):file "", line 1, in

complex("1 + 3j")valueerror: complex() arg is a malformed string>>>
熟悉c/c++等語言的讀者,都知道表示式 !a == b 等價於 (!a) == b。但對於python, not a == b 卻等價於 not (a == b)。如下所示:

>>> (not 1.5) == 1.0

false

>>> not 1.5 == 1.0

true

>>>

>>> s = [[0], ]

>>> s

[[0], ]

>>>

>>> t = s * 3

>>> t

[[0], , [0], , [0], ]

>>>

>>> s

[[0, 1], ]

>>> t

[[0, 1], , [0, 1], , [0, 1], ]

>>>

>>> s[1]["name"] = "google"

>>> s

[[0, 1], ]

>>> t

[[0, 1], , [0, 1], , [0, 1], ]

>>>

>>> t[0].remove(1)

>>> t

[[0], , [0], , [0], ]

>>> s

[[0], ]

從上面的例子,我們可以看出,對於列表 t 而言,它只是對 s 做了淺拷貝(shallow copy),t 引用了 s 中巢狀的元素(乙個 list 和 乙個 dictionary),所以導致對 s 或 t 中元素的修改,都會引起 t 或 s 中相應元素的變化。

在使用python操作檔案時,為了將內容實時更新到磁碟檔案中,需要使用 flush 函式清空快取資料,強制寫入磁碟中,但事實往往與我們的期望相悖。如下**並不能保證一定會立即寫入磁碟中:

f = open("test.txt", "w")

for i in range(1000):

f.write("a" * i)

f.write("\n")

f.flush()

f.close()

要想理解寫入磁碟,需要在flush之後,呼叫 os.fsync( ) 操作。如下所示:

import os

f = open("test.txt", "w")

for i in range(1000):

f.write("a" * i)

f.write("\n")

f.flush()

os.fsync(f.fileno())

f.close()

該「陷阱」並不是 python 獨有,與底層檔案系統有關。在python的幫助文件中,介紹 flush 函式時,也特別提示了該潛在問題。

python對小整數(-5-256)的物件,會放入乙個小整數池物件中,在該範圍內的整數,都指向該物件池的物件;超過該範圍,即大於256 或小於-5,就各自分配物件了。用語言可能不好描述,還是看下面的例子吧:

>>> a = 257

>>> b = 257

>>> a is b

false

>>>

>>> a = 256

>>> b = 256

>>> a is b

true

>>>

>>> a = -5

>>> b = -5

>>> a is b

true

>>> a = -6

>>> b = -6

>>> a is b

false

>>>

看看輸出結果啊,就知道該陷阱有多坑人啊!至於為何是這樣,只能問問python的創始人guido了啊。

Python常見陷阱

coding utf 8 python需要常見陷阱 print u 1.預設值引數 default parameter values 1.預設值引數 default parameter values 1.1 預設值引數的引入 預設值引數的作用 還可以使用預設引數 可變引數和關鍵字引數,使得函式定義出...

python迴圈刪除的陷阱

今天寫乙個指令碼的時候,發現結果總是不對,後來折騰了半天,才發現是python在遍歷刪除的時候,有乙個小小的陷阱,這也說明基礎不夠紮實啊 list 0,1,2,3,4,5,6,7,8,9,0 for i in list list.remove i print list 這是個專門寫的乙個demo,最...

Python預設引數陷阱問題

def fun a,l print l fun hhh fun mmmm fun xingkd python中的def語句在每次執行的時候都初始化乙個函式物件,這個函式物件就是我們要呼叫的函式,可以把它當成乙個一般的物件,只不過這個物件擁有乙個可執行的方法和部分屬性,對於引數中提供了初始值的引數,由...