Python里的那些坑

# Python里的那些坑

- Python是一門清晰簡(jiǎn)潔的語(yǔ)言,如果你對(duì)一些細(xì)節(jié)不了解的話,就會(huì)掉入到那些深不見(jiàn)底的“坑”里,下面,我就來(lái)總結(jié)一些Python里常見(jiàn)的坑。

## 列表創(chuàng)建和引用

### 嵌套列表的創(chuàng)建

- 使用*號(hào)來(lái)創(chuàng)建一個(gè)嵌套的list:

li = [[]] * 3

print(li)

# Out: [[], [], []]

- 通過(guò)這個(gè)方法,可以得到一個(gè)包含3個(gè)list的嵌套list,我們來(lái)給第一個(gè)list增加一個(gè)元素:

li[0].append(1)

print(li)

# Out: [[1], [1], [1]]

- 通過(guò)輸出的結(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語(yǔ)句會(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è)問(wèn)題

- 如果類屬性更改,則初始值更改。

- 如果將可變對(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è)問(wèn)題,可以這樣:

def foo(li=None):

if not li:

li = []

li.append(1)

print(li)

foo()

# Out: [1]

foo()

# Out: [1]

這雖然解決了上述的問(wèn)題,但,其他的一些對(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語(yǔ)句在遍歷對(duì)象是會(huì)生成一個(gè)迭代器,如果你在遍歷的過(guò)程中修改對(duì)象,會(huì)產(chǎn)生意想不到的結(jié)果:

alist = [0, 1, 2]

for index, value in enumerate(alist):

alist.pop(index)

print(alist)

# Out: [1]

第二個(gè)元素沒(méi)有被刪除,因?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è)問(wèn)題了,可以創(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ù)用來(lái)減少內(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

通過(guò)不斷的測(cè)試,會(huì)發(fā)現(xiàn)(-3,256)這區(qū)間的整數(shù)都返回True,有的甚至是(-8,257)。默認(rèn)情況下,[-5,256]會(huì)在解釋器第一次啟動(dòng)時(shí)創(chuàng)建并緩存,所以才會(huì)有上面的奇怪的行為。這是個(gè)很常見(jiàn)但很容易被忽略的一個(gè)坑。解決方案是始終使用equality(==)運(yùn)算符而不是 identity(is)運(yùn)算符比較值。

- Python還保留對(duì)常用字符串的引用,并且可以在比較is字符串的身份(即使用)時(shí)產(chǎn)生類似的混淆行為。

>>> 'python' is 'py' + 'thon'

True

python字符串被緩存了,所有python字符串都是該對(duì)象的引用,對(duì)于不常見(jiàn)的字符串,即使字符串相等,比較身份也會(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è)問(wèn)題:

i = 0

a = [i for i in range(3)]

print(i) # Outputs 0

類似地,for循環(huán)對(duì)于它們的迭代變量沒(méi)有私有的作用域

i = 0

for i in range(3):

pass

print(i) # Outputs 2

這種行為發(fā)生在Python 2和Python 3中。

為了避免泄漏變量的問(wèn)題,請(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

轉(zhuǎn)載自我的博客:[http://www.bugcode.cn/Python%20Pitfalls.html](http://www.bugcode.cn/Python%20Pitfalls.html)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,525評(píng)論 19 139
  • Single line comments start with a number symbol. """ Mult...
    aonebest閱讀 1,003評(píng)論 0 0
  • **2014真題Directions:Read the following text. Choose the be...
    又是夜半驚坐起閱讀 11,025評(píng)論 0 23
  • 蘇澈的行動(dòng)快于思考,還沒(méi)等那個(gè)人打開門他便像個(gè)受驚的兔子飛得沖出了廁所…… 楚昭好不容易聽(tīng)完女生對(duì)他的花癡,確定那...
    工作人員婆婆嘴閱讀 168評(píng)論 0 0
  • 1、銷售不是讓你去改變客戶,實(shí)際上你也改變不了,拼命努力要去改變客戶使他認(rèn)同你、買房子,這是不現(xiàn)實(shí)的。如果銷售員用...
    唯心光明閱讀 864評(píng)論 0 51

友情鏈接更多精彩內(nèi)容