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的值。
將a賦值給b的意思是「b現在引用的物件和a相同了」,也就是說,並沒有任何新物件產生,兩個變數引用同乙個位址裡的值罷了。[1, 2]
[1, 2]
淺拷貝與深拷貝python中的拷貝共分為三種:賦值拷貝,淺拷貝,深拷貝。
賦值拷貝就是「=」做的事情:
>>> a = [1,2,3]
>>> b = a
>>> b[0] = 4 # 對b中元素進行修改
>>> print(a)
[4, 2, 3]
如果希望讓b和a互相不干擾怎麼辦呢?可以使用python中的切片操作:
python中的淺拷貝包括:>>> a = [1,2,3]
>>> b = a[:]
>>> b[0] = 4 # 對b中元素進行修改
>>> print(a)
[1, 2, 3]
>>> id(a) # a與b的位址不同
2870775936712
>>> id(b)
2870775936648
好了,巨坑要來了。當你認為這就是真正的複製時:
>>> 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的版本上執行,但是在...