Python語言簡單易用,但容易給新入門的朋友形成一些微妙的,難以捕捉的錯誤,稍不注意就入坑了。 所以,今天給你們總結一些易犯的小錯誤,讓你輕鬆進行不踩坑的Python學習。python
一、縮進,符號和空格不正確數組
寫代碼時你們會使用縮進、對齊、空格等,其目的是爲了提升代碼的可讀性。 但在python語言中,許多功能都依賴於縮進。緩存
好比在建立一個新類時,該類中的全部內容都在聲明下縮進,決策、循環還有其它結構語句也會出現相似的狀況,閉包
若是你在代碼執行時發現問題,能夠查看一下是否使用了正確的縮進。app
來看看下面的例子,在使用IF語句時,請確保使用正確且合適的冒號和縮進,由於它們會致使語法和縮進錯誤。函數
val = 500
if val > 100
print("value is grater then 100")
File "<ipython-input-1-a271e37c300f>", line 2
if val > 100
^
SyntaxError: invalid syntax
複製代碼
在上面的代碼當中,出現了兩處錯誤:if語句後面的:缺失;下一行沒有進行正確的縮進,執行代碼出錯。學習
val = 500
if val > 100:
print("value is grater then 100")
value is grater then 100
複製代碼
當你更正上述代碼中的兩個問題後,你會發現整段代碼可以很好的運行。優化
二、錯誤使用類變量ui
class A(object):x = 1class B(A):passclass C(A):passprint( A.x, B.x, C.x)1 1 1 這裏輸出的值都是1,而後咱們試着來改變一下A.x和B.x的值看看有什麼變化。spa
B.x = 2
print (A.x, B.x, C.x)
A.x = 3
print (A.x, B.x, C.x)
1 2 1
3 2 3
複製代碼
咱們只改變了A.x,爲何C.x改變呢?
這裏須要簡單瞭解一下python的命名空間。
python中,命名空間是名字到對象映射的結合,不一樣命名空間中的名字是沒有關聯的,這種映射的實現有點相似於python中的字典。
當你名字訪問一個對象的屬性時,先從對象的命名空間尋找。若是找到了這個屬性,就返回這個屬性的值;若是沒有找到的話,則從類的命名空間中尋找,找到了就返回這個屬性的值,找不到則拋出異常。
在Python中,類變量在內部做爲字典處理,並遵循一般稱爲方法解析順序(MRO)的方法。
MRO:Method Resolution Order 方法解析順序,Python支持多繼承,該方法用於解決父類存在同名函數的時存在的二義性問題。
所以在上面的代碼中,因爲x在對象的命名空間中找不到該屬性C,所以將在類中查找它。換句話說,C沒有本身的x屬性,獨立於A。所以,引用C.x其實是指A.x。
三、誤解python範圍規則
若是你不瞭解python的範圍規則,那麼你很容易犯錯誤,這是由於Python使用一種獨有的範圍規則來肯定變量範圍。 python範圍解析是基於LEGB規則,如下是Python範圍規則的概述:
LEGB規則指定名稱空間的如下順序,用於搜索名稱:
Local - > Enclosed - > Global - > Built-in
複製代碼
考慮如下的例子:
x = 10
def foo():
x += 1
print(x)
foo()
#Python學習交流QQ羣:857662006
UnboundLocalError Traceback (most recent call last):
<ipython-input-26-234e54482865> in <module>
<ipython-input-26-234e54482865> in foo()
UnboundLocalError: local variable 'x' referenced before assignment
複製代碼
發生上述錯誤的緣由是,對做用域中的變量進行賦值時,Python會自動將該變量視爲該做用域的本地變量,並在外部做用域中隱藏任何相似命名的變量。 所以,許多人在代碼提示出錯並顯示須要在函數中添加賦值語句而感到不解。 考慮一個在使用列表時遇到的例子:
lst = [1, 2, 3]
def foo1():
lst.append(5)
foo1()
lst
[1, 2, 3, 5]
lst = [1, 2, 3]
def foo2():
lst += [5]
foo2()
UnboundLocalError Traceback (most recent call last):
<ipython-input-30-579469eed71a> in <module>
<ipython-input-30-579469eed71a> in foo2()
UnboundLocalError: local variable 'lst' referenced before assignment
複製代碼
爲何foo2出錯了可是foo1運行良好?
答案在前面就已經有所提示,在這個例子當中foo1()作一個分配到lst,而在foo2()當中lst += [5]其實只是lst = lst + [5]的簡寫,咱們但願分配一個值給lst,可是分配的值lst是基於lst自身,但其還沒有定義。
四、python閉包變量綁定
python的閉包變量問題也是新手們容易混淆的一個點,來看看下面的例子:
def create_multipliers():
return [lambda x : i * x for i in range(5)]
for multiplier in create_multipliers():
print (multiplier(2))
8
8
8
8
8
複製代碼
爲何結果是88888,和我所想的02468不同呢?
這是因爲Python的遲綁定(late binding)機制,閉包中內部函數的值只有在被調用時纔會進行查詢。
所以create_multipliers函數返回的lambda函數被調用時,會在附近的做用域中查詢變量i的值,而在create_multipliers生成返回數組以後,整數i的值是4,不會再改變,所以返回數組中每一個匿名函數實際上都是:lambda x: 4*x。、
解決辦法是將臨時值也保存在匿名函數的做用域內,在聲明匿名函數時就查詢變量的值。 瞭解原理以後,讓咱們來改一改代碼,surprise!
def create_multipliers():
return [lambda x, i=i : i * x for i in range(5)]
for multiplier in create_multipliers():
print (multiplier(2))
0
2
4
6
8
複製代碼
五、名稱與Python標準庫模塊發生衝突
Python擁有大量的庫模塊,開箱即用。可是,若是您遇到一個模塊的名稱與Python附帶的標準庫中具備相同名稱的模塊之間的名稱衝突,則可能會出現問題。
例如導入另外一個庫,而這個庫又會嘗試導入模塊的Python標準庫版本,但因爲你有一個同名的模塊,另外一個包會錯誤地導入你的版本而不是Python標準庫。
所以,應該注意避免使用與Python標準庫模塊中相同的名稱,而且更改包中的模塊名稱比提交Python Enhancement Proposal(PEP)以請求名稱更改更容易。
六、is和==/=和==
Python中有不少運算符,例如is,=,==這三個,許多剛剛入門的新手會誤解這三個運算符的意義和用法,以至於代碼出錯。
在 Python 中會用到對象之間比較,能夠用 ==,也能夠用 is,但對對象比較判斷的內容並不相同,區別在哪裏?
·is 比較兩個對象的 id 值是否相等,是否指向同一個內存地址,== 比較的是兩個對象的內容是否相等,值是否相等;
a = ["Python"]
b = a
b is a
True
id(a)
2222222
id(b)
2222222
b == a
True
複製代碼
能夠發現上面的例子當中b和a的內存地址是相同的,它們指向同一塊內存,於是 is 和 == 的結果都爲True,這是由於直接賦值都是賦值的引用。若是新建對象以後,b 和 a 指向了不一樣的內存,那麼 b is a 的結果爲False,而 b==a的結果爲True。
·小整數對象[-5,256]在全局解釋器範圍內被放入緩存供重複使用,例如:
a = 1
b = 1
a is b
True
a == b
True
a = 257
b = 257
a is b
False
複製代碼
Python僅僅對比較小的整數對象進行緩存(範圍爲範圍[-5, 256])緩存起來,而並不是是全部整數對象。須要注意的是,這僅僅是在命令行中執行,而在Pycharm或者保存爲文件執行,結果是不同的,這是由於解釋器作了一部分優化。
=和==的含義不一樣:
=表明的含義是賦值,將某一數值賦給某個變量,好比a=3,將3這個數值賦予給a。 ==是判斷是否相等,返回True或False,好比1==1。他們是相等的,那麼就返回true。1==2,他們是不相等的,那麼就返回false。 例子:
a = [1,2]
b = [1,2]
c = a
a is b
False
a is c
true
a == b
true
複製代碼
七、濫用__init__
__init__方法在Python中用做構造函數,當Python將內存分配給新的類對象時,它會自動被調用。 首先,__init__並不至關於C#中的構造函數,在執行它的時候,實例已經構造出來。
#小編建立了一個Python學習交流QQ羣:857662006
class A(object):
def __init__(self,name):
self.name=name
def getName(self):
return 'A '+self.name
執行代碼:
a=A('hello')
複製代碼
能夠理解爲:
a=object.__new__(A)
A.__init__(a,'hello')
複製代碼
即__init__做用是初始化已實例化後的對象。
其次,子類能夠不重寫__init__,實例化子類時,會自動調用超類中已定義的__init__。
class B(A):
def getName(self):
return 'B '+self.name
if __name__=='__main__':
b=B('hello')
print (b.getName())
複製代碼
但若是重寫了__init__,實例化子類時,則不會隱式的再去調用超類中已定義的__init__。
class C(A):
def __init__(self):
pass
def getName(self):
return 'C '+self.name
if __name__=='__main__':
c=C()
print (c.getName())
複製代碼
此時執行代碼則會報"AttributeError: 'C' object has noattribute 'name'」錯誤,因此若是重寫了__init__,爲了能使用或擴展超類中的行爲,最好顯式的調用超類的__init__方法。
class C(A):
def __init__(self,name):
super(C,self).__init__(name)
def getName(self):
return 'C '+self.name
if __name__=='__main__':
c=C('hello')
print (c.getName())
複製代碼