Python中一些深不見底的“坑”
Python是一門清晰簡(jiǎn)潔的語言,如果你對(duì)一些細(xì)節(jié)不了解的話,就會(huì)掉入到那些深不見底的“坑”里,下面,我就來總結(jié)一些Python里常見的坑。
列表創(chuàng)建和引用
嵌套列表的創(chuàng)建
使用*號(hào)來創(chuàng)建一個(gè)嵌套的list:
li = [[]] * 3 print(li) # Out: [[], [], []]
通過這個(gè)方法,可以得到一個(gè)包含3個(gè)list的嵌套list,我們來給第一個(gè)list增加一個(gè)元素:
li[0].append(1) print(li) # Out: [[1], [1], [1]]
通過輸出的結(jié)果可以看初,我們只給第一元素增加元素,結(jié)果三個(gè)list都增加了一個(gè)元素。這是因?yàn)閇[]]*3并不是創(chuàng)建了三個(gè)不同list,而是創(chuàng)建了三個(gè)指向同一個(gè)list的對(duì)象,所以,當(dāng)我們操作第一個(gè)元素時(shí),其他兩個(gè)元素內(nèi)容也會(huì)發(fā)生變化的原因。效果等同于下面這段代碼:
li = [] element = [[]] li = element + element + element print(li) # Out: [[], [], []] element.append(1) print(li) # Out: [[1], [1], [1]]
我們可以打印出元素的內(nèi)存地址一探究竟:
li = [[]] * 3 print([id(inner_list) for inner_list in li]) # Out: [6830760, 6830760, 6830760]
到這我們可以明白原因了。那如何解決了?可以這樣:
li = [[] for _ in range(3)]
這樣我們就創(chuàng)建了三個(gè)不同的list對(duì)象
print([id(inner_list) for inner_list in li]) # Out: [6331048, 6331528, 6331488]
列表元素的引用
不要使用索引方法遍歷list,例如:
for i in range(len(tab)):
print(tab[i])
比較好的方法是:
for elem in tab: print(elem)
for語句會(huì)自動(dòng)生成一個(gè)迭代器。如果你需要索引位置和元素,使用enumerate函數(shù):
for i, elem in enumerate(tab):
print((i, elem))
注意 == 符號(hào)的使用
if (var == True):
# 當(dāng)var是:True、1、 1.0、 1L時(shí)if條件成立
if (var != True):
# 當(dāng)var不是 True 和 1 時(shí)if條件成立
if (var == False):
# 當(dāng)var是 False 或者 0 (or 0.0, 0L, 0j) if條件成立
if (var == None):
# var是None if條件成立
if var:
# 當(dāng)var非空(None或者大小為0)對(duì)象 string/list/dictionary/tuple, non-0等if條件成立
if not var:
# 當(dāng)var空(None或者大小為0)對(duì)象 string/list/dictionary/tuple, non-0等if條件成立
if var is True:
# 只有當(dāng)var時(shí)True時(shí) if條件成立 1也不行
if var is False:
# 只有當(dāng)var時(shí)False時(shí) if條件成立 0也不行
if var is None:
# 和var == None 一致
捕獲異常由于提前檢查
不夠優(yōu)雅的代碼:
if os.path.isfile(file_path):
file = open(file_path)
else:
# do something
比較好的做法:
try:
file = open(file_path)
except OSError as e:
# do something
在python2.6+的里面可以更簡(jiǎn)潔:
with open(file_path) as file:
之所以這么用,是這么寫更加通用,比如file_path給你傳個(gè)None就瞎了,還得判斷是不是None,如果不判斷,就又得抓異常,判斷的話,代碼有多寫了很多。
類變量初始化
不要在對(duì)象的init函數(shù)之外初始化類屬性,主要有兩個(gè)問題
- 如果類屬性更改,則初始值更改。
- 如果將可變對(duì)象設(shè)置為默認(rèn)值,您將獲得跨實(shí)例共享的相同對(duì)象。
錯(cuò)誤示范(除非你想要靜態(tài)變量)
```
class Car(object):
color = "red"
wheels = [Wheel(), Wheel(), Wheel(), Wheel()]
```
正確的做法:
```
class Car(object):
def __init__(self):
self.color = "red"
self.wheels = [Wheel(), Wheel(), Wheel(), Wheel()]
```
**函數(shù)默認(rèn)參數(shù)**
```
def foo(li=[]):
li.append(1)
print(li)
foo([2])
# Out: [2, 1]
foo([3])
# Out: [3, 1]
```
該代碼的行為與預(yù)期的一樣,但如果我們不傳遞參數(shù)呢?
``` foo() # Out: [1] As expected... foo() # Out: [1, 1] Not as expected... ```
這是因?yàn)楹瘮?shù)參數(shù)類型是定義是確認(rèn)的而不是運(yùn)行時(shí),所以在兩次函數(shù)調(diào)用時(shí),li指向的是同一個(gè)list對(duì)象,如果要解決這個(gè)問題,可以這樣:
```
def foo(li=None):
if not li:
li = []
li.append(1)
print(li)
foo()
# Out: [1]
foo()
# Out: [1]
```
這雖然解決了上述的問題,但,其他的一些對(duì)象,比如零長(zhǎng)度的字符串,輸出的結(jié)果就不是我們想要的。
``` x = [] foo(li=x) # Out: [1] foo(li="") # Out: [1] foo(li=0) # Out: [1] ```
最常用的辦法是檢查參數(shù)是不是None
```
def foo(li=None):
if li is None:
li = []
li.append(1)
print(li)
foo()
# Out: [1]
```
**在遍歷時(shí)修改**
for語句在遍歷對(duì)象是會(huì)生成一個(gè)迭代器,如果你在遍歷的過程中修改對(duì)象,會(huì)產(chǎn)生意想不到的結(jié)果:
alist = [0, 1, 2]
for index, value in enumerate(alist):
alist.pop(index)
print(alist)
# Out: [1]
第二個(gè)元素沒有被刪除,因?yàn)榈错樞虮闅v索引。上述循環(huán)遍歷兩次,結(jié)果如下:
# Iteration #1 index = 0 alist = [0, 1, 2] alist.pop(0) # removes '0' # Iteration #2 index = 1 alist = [1, 2] alist.pop(1) # removes '2' # loop terminates, but alist is not empty: alist = [1]
如果避免這個(gè)問題了,可以創(chuàng)建另外一個(gè)list
alist = [1,2,3,4,5,6,7]
for index, item in reversed(list(enumerate(alist))):
# delete all even items
if item % 2 == 0:
alist.pop(index)
print(alist)
# Out: [1, 3, 5, 7]
整數(shù)和字符串定義
python預(yù)先緩存了一個(gè)區(qū)間的整數(shù)用來減少內(nèi)存的操作,但也正是如此,有時(shí)候會(huì)出很奇特的錯(cuò)誤,例如:
>>> -8 is (-7 - 1) False >>> -3 is (-2 - 1) True
另外一個(gè)例子
>>> (255 + 1) is (255 + 1) True >>> (256 + 1) is (256 + 1) False
通過不斷的測(cè)試,會(huì)發(fā)現(xiàn)(-3,256)這區(qū)間的整數(shù)都返回True,有的甚至是(-8,257)。默認(rèn)情況下,[-5,256]會(huì)在解釋器第一次啟動(dòng)時(shí)創(chuàng)建并緩存,所以才會(huì)有上面的奇怪的行為。這是個(gè)很常見但很容易被忽略的一個(gè)坑。解決方案是始終使用equality(==)運(yùn)算符而不是 identity(is)運(yùn)算符比較值。
Python還保留對(duì)常用字符串的引用,并且可以在比較is字符串的身份(即使用)時(shí)產(chǎn)生類似的混淆行為。
>>> 'python' is 'py' + 'thon' True
python字符串被緩存了,所有python字符串都是該對(duì)象的引用,對(duì)于不常見的字符串,即使字符串相等,比較身份也會(huì)失敗。
>>> 'this is not a common string' is 'this is not' + ' a common string' False >>> 'this is not a common string' == 'this is not' + ' a common string' True
所以,就像整數(shù)規(guī)則一樣,總是使用equal(==)運(yùn)算符而不是 identity(is)運(yùn)算符比較字符串值。
列表推導(dǎo)和循環(huán)中的變量泄漏
有個(gè)例子:
i = 0 a = [i for i in range(3)] print(i) # Outputs 2
python2中列表推導(dǎo)改變了i變量的值,而python3修復(fù)了這個(gè)問題:
i = 0 a = [i for i in range(3)] print(i) # Outputs 0
類似地,for循環(huán)對(duì)于它們的迭代變量沒有私有的作用域
i = 0
for i in range(3):
pass
print(i) # Outputs 2
這種行為發(fā)生在Python 2和Python 3中。
為了避免泄漏變量的問題,請(qǐng)?jiān)诹斜硗茖?dǎo)和for循環(huán)中使用新的變量。
or操作符
例如
if a == 3 or b == 3 or c == 3:
這個(gè)很簡(jiǎn)單,但是,再看一個(gè):
if a or b or c == 3: # Wrong
這是由于or的優(yōu)先級(jí)低于==,所以表達(dá)式將被評(píng)估為if (a) or (b) or (c == 3):。正確的方法是明確檢查所有條件:
if a == 3 or b == 3 or c == 3: # Right Way
或者,可以使用內(nèi)置函數(shù)any()代替鏈接or運(yùn)算符:
if any([a == 3, b == 3, c == 3]): # Right
或者,為了使其更有效率:
if any(x == 3 for x in (a, b, c)): # Right
更加簡(jiǎn)短的寫法:
if 3 in (a, b, c): # Right
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
- python中from module import * 的一個(gè)坑
- Python創(chuàng)建二維數(shù)組實(shí)例(關(guān)于list的一個(gè)小坑)
- Python安裝lz4-0.10.1遇到的坑
- 淺析python中SQLAlchemy排序的一個(gè)坑
- python使用scrapy發(fā)送post請(qǐng)求的坑
- Python編碼爬坑指南(必看)
- python list刪除元素時(shí)要注意的坑點(diǎn)分享
- 詳談python3中用for循環(huán)刪除列表中元素的坑
- 淺談python配置與使用OpenCV踩的一些坑
- python with提前退出遇到的坑與解決方案
相關(guān)文章
Python利用內(nèi)置庫(kù)實(shí)現(xiàn)數(shù)據(jù)的加密與校驗(yàn)
這篇文章主要為大家詳細(xì)介紹了如何使用Python內(nèi)置庫(kù)實(shí)現(xiàn)數(shù)據(jù)的加密和校驗(yàn),為開發(fā)者提供全方位的數(shù)據(jù)安全解決方案,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12
Python運(yùn)維開發(fā)之psutil庫(kù)的使用詳解
這篇文章主要介紹了Python運(yùn)維開發(fā)之psutil庫(kù)的使用,psutil能夠輕松實(shí)現(xiàn)獲取系統(tǒng)運(yùn)行的進(jìn)程和系統(tǒng)利用率。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-10-10
Python高級(jí)property屬性用法實(shí)例分析
這篇文章主要介紹了Python高級(jí)property屬性用法,結(jié)合實(shí)例形式分析了Python property屬性的功能及各種常見的使用技巧,需要的朋友可以參考下2019-11-11
安裝ElasticSearch搜索工具并配置Python驅(qū)動(dòng)的方法
這篇文章主要介紹了安裝ElasticSearch搜索工具并配置Python驅(qū)動(dòng)的方法,文中還介紹了其與Kibana數(shù)據(jù)顯示客戶端的配合使用,需要的朋友可以參考下2015-12-12

