程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

相見恨晚!Python那些不為人知的大坑

編輯:Python

”Python容易“似乎已經成了很多開發者的共識,的確,它的表達方式已經非常接近自然語言。不用像C++那樣去考慮指針、內存,也不用像Java那樣深入理解JVM。

慢慢的,開發者開始確切的認為”我們寫的代碼是完全正確的“。

但是,在忽視Python的細節的過程中,會發現,會出現奇奇怪怪的錯誤。回頭定位時,遲遲定位不出問題,在網絡上搜索也找不到對應的解決方案,回頭看代碼,覺得代碼無懈可擊。殊不知,在開發過程中已經犯了很多錯誤。

本文,就總結5個Python初學者經常會遇到的錯誤。不瞞各位,其中有些錯誤我當初也踩過坑,並且遲遲無法逃脫,希望把這些問題總結出來,避免大家再次踩坑!

1. 字典拷貝

有些場景下,需要對字典拷貝一個副本。這個副本用於保存原始數據,然後原來的字典去參與其他運算,或者作為參數傳遞給一些函數。

例如,

>>> dict_a = {"name": "John", "address":"221B Baker street"}
>>> dict_b = dict_a

利用賦值運算法把dict_a賦值給dict_b之後,這2個變量的值是相同的。

你或許會拿著dict_b去參與其他的運算,例如,更新/添加鍵值對。

但事實卻不是你認為的那樣,如果你更新或者編輯dict_bdict_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}

2. 字典鍵值

首先,來看一個示例,

>>> 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

3. 更新列表或字典

同樣,先看一個列表的示例:

>>> 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_adict_a竟然沒有任何內容輸出!

這是因為,在Python中列表、字典中的一些方法,如排序、更新、附加、添加等,不會創建不必要的副本,從而提升性能,因此,不需要重新分配給變量。

再看一下正確的方法:

>>> list_a = [1,2,3,4,5]
>>> list_a.append(6)
>>> list_a.sort()
>>> list_a
[1, 2, 3, 4, 5, 6]

4. 駐留字符串

在某些情況下,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

5. 默認參數只計算一次

先看一下下面這個示例:

>>> 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有深入的了解,自然會避免這些錯誤。反之,則會給你帶來大麻煩,一旦出了錯誤就很難定位出來。


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved