”Python容易“似乎已經成了很多開發者的共識,的確,它的表達方式已經非常接近自然語言。不用像C++那樣去考慮指針、內存,也不用像Java那樣深入理解JVM。
慢慢的,開發者開始確切的認為”我們寫的代碼是完全正確的“。
但是,在忽視Python的細節的過程中,會發現,會出現奇奇怪怪的錯誤。回頭定位時,遲遲定位不出問題,在網絡上搜索也找不到對應的解決方案,回頭看代碼,覺得代碼無懈可擊。殊不知,在開發過程中已經犯了很多錯誤。
本文,就總結5個Python初學者經常會遇到的錯誤。不瞞各位,其中有些錯誤我當初也踩過坑,並且遲遲無法逃脫,希望把這些問題總結出來,避免大家再次踩坑!
有些場景下,需要對字典拷貝一個副本。這個副本用於保存原始數據,然後原來的字典去參與其他運算,或者作為參數傳遞給一些函數。
例如,
>>> dict_a = {"name": "John", "address":"221B Baker street"} >>> dict_b = dict_a
利用賦值運算法把dict_a
賦值給dict_b
之後,這2個變量的值是相同的。
你或許會拿著dict_b
去參與其他的運算,例如,更新/添加鍵值對。
但事實卻不是你認為的那樣,如果你更新或者編輯dict_b
,dict_a
也會隨之改變,詳細內容可以了解一下Python可變對象與不可變對象。
下面來看一下效果:
>>> dict_b["age"] = 26 >>> dict_b {'address': '221B Baker street', 'name': 'John', 'age': 26} >>> dict_a {'address': '221B Baker street', 'name': 'John', 'age': 26}
你會發現,給dict_b
添加了一個age:26
鍵值對,dict_a
也更新了,這樣,我們留一個副本就沒有任何意義了。
針對這個問題,可以用Python中的淺拷貝copy
、深拷貝deepcopy
來解決,下面來看一下,
>>> dict_c = dict_b.copy() >>> dict_c["location"] = "somewhere" >>> dict_c {'address': '221B Baker street', 'name': 'John', 'age': 26, 'location': 'somewhere'} >>> dict_b {'address': '221B Baker street', 'name': 'John', 'age': 26}
首先,來看一個示例,
>>> dict_a = dict() >>> dict_a {} >>> dict_a[1] = "apple" >>> dict_a[True] = "mango" >>> dict_a[2] = "melon" >>> dict_a {1: 'mango', 2: 'melon'}
你注意到發生了什麼嗎?
輸出字典之後,發現鍵值True
沒有了!
這是因為,在Python中,True相當於1、False相當於0,因此,在dict_a[True] = "mango"
這行代碼裡,它把原來鍵值為1
給替換了。
可以來驗證一下True相當於1的說法:
>>> isinstance(True, int) True >>> True == 1 True
同樣,先看一個列表的示例:
>>> list_a = [1,2,3,4,5] >>> list_a = list_a.append(6) >>> list_a # 不輸出任何內容
再看一個字典的示例:
>>> dict_a = {"a" : "b"} >>> dict_a = dict_a.update({"c" : "d"}) >>> dict_a # 不輸出任何內容
發現,打印list_a
和dict_a
竟然沒有任何內容輸出!
這是因為,在Python中列表、字典中的一些方法,如排序、更新、附加、添加等,不會創建不必要的副本,從而提升性能,因此,不需要重新分配給變量。
再看一下正確的方法:
>>> list_a = [1,2,3,4,5] >>> list_a.append(6) >>> list_a.sort() >>> list_a [1, 2, 3, 4, 5, 6]
在某些情況下,Python嘗試重用現有的不可變對象。字符串駐留就是這樣一種情況。
來看一個示例對比,
>>> a = "gmail" >>> b = "gmail" >>> a is b True
然後修改一下,
>>> a = "@gmail" >>> b = "@gmail" >>> a is b False
是不是很神奇?我們只加了一個@
符號,結果卻截然不同!
在第一個實現方法中,嘗試創建兩個不同的字符串對象。但是當檢查兩個對象是否相同時,它返回True。
這是因為python並沒有創建另一個對象b,而是將b指向了第一個值gmail
,換句話說它被駐留了。
但是,如果字符串中除ASCII字符、數字、下劃線以外的其他字符時,它則不會駐留,這樣的話,它就不會再指向@gmail
。
這裡需要注意一下,is
與==
的運算是不同的。
==
用於判斷值是否相等,is
不僅需要值相等,還需要指向同一個對象。
>>> a = "@gmail" >>> b = "@gmail" >>> a is b False >>> a == b True
先看一下下面這個示例:
>>> def func(a, lst=[]): ... lst.append(a) ... return lst ... >>> print(func(1)) [1] >>> print(func(2)) [1, 2]
這裡發生了什麼?
我在func
中給了一個默認參數[]
,然後先後調用2次func
函數。
按照我們常規的認識,這2次調用是分開的,第1次調用輸出[1]
,第二次應該輸出[2]
,為什麼第2次調用時列表裡竟然還保留著第1次調用時的值?
這是因為,在Python中,默認參數只會被計算一次。第1次調用func(1)
時,它用到了默認參數。但是,第2次調用就不會再去計算默認參數,直接在[1]
的基礎上附加一個值。
本文介紹的這些經常犯錯的點,或許你還沒有遇到過,因此會覺得不以為然。但是,總會在某個時間點、開發某個小模塊時不知不覺中用到這5個問題所涵蓋的其中某個功能。
如果想當然,按照我們概念裡認為的那樣去做,結果可想而知,會出現錯誤。
因此,在Python開發過程中,應該注意一些細節,深入系統的學習一下Python,避免在一些Python基本特性方面犯一些錯誤。
如果你對Python有深入的了解,自然會避免這些錯誤。反之,則會給你帶來大麻煩,一旦出了錯誤就很難定位出來。