那些年在使用python過程中踩的一些坑。

2021-10-06 18:56:49 字數 4077 閱讀 4317

python是一門功能非常強大,語法也比較簡單的程式語言。在使用python的過程中本人深深地感受到這門語言的魅力。

即便如此,本人在程式設計的過程中依舊踩到了一些坑。這裡將它們簡單總結起來,希望可以幫助一些新人規避這些問題。

當然最好的辦法還是在學習語言的過程中更留意語法細節。:)

注意你所使用的資料型別:對不同資料型別執行同乙個操作可能會得到不同結果:python共有兩種資料型別:

在python世界中,一切變數皆為物件的引用。

變數引用一塊記憶體位址中儲存的值,它本身並不儲存值。

對於不可變資料型別來說,不能通過變數來修改這個值。

舉個例子:

>>> a = "hello"  

>>> id(a) # 返回位址

1582645074160

>>> b = "hello"

>>> id(b)

1582645074160

>>> b = "what"

>>> id(b)

1582645072760

>>> id(a)

1582645074160

a和b都同樣引用乙個字串物件"hello",該值儲存在記憶體位址4160中。但當給b賦新值"what"時,由於不能改變當前值"hello",系統會分配給它另外乙個位址2760,該位址儲存"what"。於是b和a不再指向同乙個位址的值。

還需要注意的一點是,多個變數引用同乙個物件時,若要給其中乙個變數賦另外乙個值,實際上並不是改變原來位址裡儲存的值,而是重新開闢一塊記憶體空間給它,於是修改其中乙個變數不會影響其它變數的取值。

>>> a = 10

>>> b = a

>>> b += 2

>>> print(a,b)

10 12

(對於元組來說,相同的元組可能位址不同,但它是唯讀的,所以依然是不可變型別)

對於可變資料型別來說,可以通過變數修改值

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

>>> id(a)

2763060425160

>>> b = [1,2,3]

>>> id(b)

2763062998728

>>> id(b)

2763062998728

a和b同樣引用列表[1,2,3],但可以看出這兩個相同的列表位址卻是不同的。因此對於可變資料型別來說,具有相同值的物件不是同乙個物件。並且對b做出修改以後,b的位址並沒有變。也就是說,可變資料型別允許修改當前位址下儲存的值。

於是乎,可變資料型別會帶來下面乙個坑:

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

>>> b = a

>>> print(a,b)

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

當a和b引用同乙個可變型別物件時,改變其中乙個,另乙個也會隨之發生改變,因為它們引用同乙個位址的值。

總結:

不可變型別相同值儲存位址相同(元組除外),修改變數時並不改變當前位址儲存的值,而是給它乙個新的位址。當多個變數引用同乙個值時,改變其中乙個變數的取值不影響其他變數。

可變型別相同值儲存位址可能不同,可以通過變數修改當前位址儲存的值。當多個變數指向同乙個值時,改變其中乙個變數的取值,其他變數會跟著一起改變。

函式預設引數值不要使用可變資料型別先來看一段**:

class test(object):

def __init__(self,lst=):

self.lst = lst

if __name__ == "__main__":

a = test()

b = test()

print(a.lst)

print(b.lst)

定義乙個test類,並定義其建構函式的預設引數lst為乙個列表。看著挺好的,然後我們建立兩個物件a和b。修改a中的字段lst,向裡面新增兩個元素,b保持預設狀態就好。

現在執行這段**,輸出a和b中lst的值。

[1, 2]

[1, 2]

淺拷貝與深拷貝python中的拷貝共分為三種:賦值拷貝,淺拷貝,深拷貝。

賦值拷貝就是「=」做的事情:

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

>>> b = a

>>> b[0] = 4 # 對b中元素進行修改

>>> print(a)

[4, 2, 3]

將a賦值給b的意思是「b現在引用的物件和a相同了」,也就是說,並沒有任何新物件產生,兩個變數引用同乙個位址裡的值罷了。

如果希望讓b和a互相不干擾怎麼辦呢?可以使用python中的切片操作:

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

>>> b = a[:]

>>> b[0] = 4 # 對b中元素進行修改

>>> print(a)

[1, 2, 3]>>> id(a) # a與b的位址不同

2870775936712

>>> id(b)

2870775936648

python中的淺拷貝包括:

好了,巨坑要來了。當你認為這就是真正的複製時:

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

>>> b = a[:]

>>> id(a) # a和b位址不同

2870773363144

>>> id(b)

2870775950280

>>> print(a)

[1, 2, 3, [4, 5, 6]] # a怎麼又變了??

>>> id(a[3]) # a和b中的元素子列表位址相同

2870775936712

>>> id(b[3])

2870775936712

當改變列表中的子列表[4,5]時,a的值又隨著b改變了。這是為什麼呢?

所謂淺拷貝,實際上是「建立乙個新的物件,這個物件內部元素與之前一樣」,也就是說,a和b指向兩個不同物件,但這兩個物件的內部元素都相同!所以對於a和b來說,儘管他倆本身的位址不同,但是他們內部的元素位址是一樣的。

此時,列表裡面有兩種型別,乙個是整數型別的1,2和3,還有乙個列表[4,5],前面已經說過,整數型別是不可變型別,所以修改b中的整數型別對a沒有影響,但是如果修改b中的子列表[4,5],由於兩個列表元素的位址都一樣,b的改動勢必會影響a。

如何徹徹底底地複製拷貝乙個物件呢,答案是深拷貝:

>>> from copy import deepcopy

>>> a = [1,2,3,[4,5,6]]

>>> b =deepcopy(a)

>>> id(a) # a和b位址不同

2870775950280

>>> id(b)

2870775936584

>>> print(a)

[1, 2, 3, [4, 5, 6]] # b:這回拜拜了您

這回a和b是妥妥的互不干擾了,因為這次不僅列表物件本身是新的,物件裡面的元素也都是新的。

>>> id(a[0])  # a和b中的首元素1位址相同

1943694400

>>> id(b[0])

1943694400

>>> id(a[3]) # a和b中的尾元素子列表[4,5,6]位址不同

2870775950344

>>> id(b[3])

2870775961864

有趣的是,儘管子列表位址不再相同,對於第乙個元素1來說位址依舊相同。但是沒關係啊,因為1是不可變型別。深拷貝只拷貝可變型別元素,而不可變型別元素本身就互不干擾,也就不需要拷貝。

以上是我在使用python中遇到過的坑,之後會持續更新,希望能幫助到大家。

python使用過程中問題

1.檢視python支援的 whl格式 在cmd輸入python 或者 python3.6 import pip print pip.pep425tags.get supported 2.在修改python.exe為python36.exe 任何重新命名 後,pip會報錯 fatal error i...

Python學習過程中那些很重要有容易忽略的細節

本文我將持續更新和補充,可以收藏。主要記錄 開發過程中程式不報錯但是邏輯存在問題的 邏輯會報錯但是 太簡單 不太會去優先測試的 及其他各種 當下比較粗糙,以後積累到一定數量,希望能做成乙個手冊。1.中英文標點符號錯誤,尤其引號,逗號,冒號是否落下,是否錯誤。尤其在input函式 正規表示式中等。這個...

BERT使用過程中的碰到的那些報錯

bert是谷歌2018年提出的語言模型,在十幾個任務上達到了state of art。在這裡本人在使用過程中總結了一下遇到的錯誤。bert推薦在tpu上執行,但是資源有限在gpu上跑也行,不行也能在cpu上跑 ps就是有些慢 官方bert的版本建議在tensorflow 1.11的版本上執行,但是在...