![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
Python以其簡單的語法而聞名。然而,當您第一次學習Python時,或者當您具備另外一種編程語言的堅實背景時,您可能會遇到一些Python不容許的事情。若是您在嘗試運行Python代碼時收到過SyntaxError錯誤,那麼本指南能夠幫助您。在本教程中,您將看到Python中常見的無效語法示例,並學習如何解決這個問題。python
在本教程結束時,您將可以:web
識別Python中的無效語法shell
理解SyntaxError回溯編程
解析無效語法或徹底阻止它微信
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
當您運行Python代碼時,解釋器將首先解析它,將其轉換成Python字節碼,而後執行。解釋器將在程序執行的第一階段(也稱爲解析階段)中發現Python中的任何無效語法。若是解釋器不能成功地解析您的Python代碼,那麼這意味着您在代碼的某個地方使用了無效的語法。解釋器將嘗試向您顯示錯誤發生的位置。app
當您第一次學習Python時,獲得一個SyntaxError可能會使人沮喪。Python將嘗試幫助您肯定無效語法在代碼中的位置,可是它提供的回溯可能會讓您感到有些困惑。有時,它所指向的代碼是徹底正確的。機器學習
您不能像處理其餘異常同樣處理Python中的無效語法。即便您嘗試將try和except塊封裝到帶有無效語法的代碼中,您仍然會看到解釋器拋出一個SyntaxError。編程語言
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
當解釋器在Python代碼中遇到無效語法時,它將拋出一個SyntaxError異常,並提供一個帶有一些有用信息的回溯,以幫助您調試錯誤。下面是一些Python中包含無效語法的代碼:編輯器
1 # theofficefacts.py
2 ages = {
3 'pam': 24,
4 'jim': 24
5 'michael': 43
6 }
7 print(f'Michael is {ages["michael"]} years old.')
您能夠在第4行字典的文字中看到無效的語法。第二個詞條「jim」漏掉了一個逗號。若是你試着按原樣運行這段代碼,你會獲得如下回溯結果:
ide
$ python theofficefacts.py
File "theofficefacts.py", line 5
'michael': 43
^
SyntaxError: invalid syntax
注意,traceback消息定位的錯誤在第5行,而不是第4行。Python解釋器試圖指出無效語法的位置。然而,它只能指出它最初注意到的問題。當您得到一個SyntaxError traceback,而且traceback所指向的代碼看起來很好,那麼您將但願開始向後移動代碼,直到您可以肯定哪裏出了問題。
在上面的例子中,根據後面的內容,省略逗號是沒有問題的。例如,第5行「michael」後面缺乏逗號是沒有問題的。可是一旦解釋器遇到不理解的東西,它只能指出它發現的第一件不理解的事情。
回溯是一個堆棧跟蹤,從異常處理程序的點一直到調用鏈中的異常引起點。您還能夠從調用的角度(而且沒有錯誤的上下文)向上使用當前調用堆棧,這對於查找函數所遵循的路徑很是有用。
有幾個元素的SyntaxError回溯,能夠幫助您肯定無效的語法在您的代碼:
遇到無效語法的文件名
遇到問題的行號和代碼的複寫行
在複製代碼下面的行中有一個插入符號(^),它向您顯示代碼中有問題的那一點
異常類型SyntaxError以後的錯誤消息,能夠提供幫助您肯定問題的信息
在上面的例子中,給出的文件名是theofficefacts。行號爲5,插入符號指向字典鍵michael的結束引用。SyntaxError回溯可能不會指向真正的問題,但它將指向解釋器沒法理解語法的第一個地方。
您可能會看到Python引起另外兩個異常。它們等價於SyntaxError,但有不一樣的名稱:
IndentationError
TabError
這些異常都繼承自SyntaxError類,但它們是涉及縮進的特殊狀況。當代碼的縮進級別不匹配時,將引起IndentationError。當代碼在同一文件中同時使用製表符和空格時,將引起一個製表符錯誤。在後面的小節中,您將進一步瞭解這些異常。
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
當您第一次遇到SyntaxError時,瞭解爲何會出現問題以及如何修復Python代碼中的無效語法是頗有幫助的。在下面的小節中,您將看到可能引起SyntaxError的一些更常見的緣由,以及如何修復它們。
在Python中有幾種狀況下,您不能對對象進行賦值。一些例子是分配文字和函數調用。在下面的代碼塊中,您能夠看到一些嘗試這樣作的示例和由此產生的SyntaxError回溯:
>>>
>>> len('hello') = 5
File "<stdin>", line 1
SyntaxError: can't assign to function call
>>> 'foo' = 1
File "<stdin>", line 1
SyntaxError: can't assign to literal
>>> 1 = 'foo'
File "<stdin>", line 1
SyntaxError: can't assign to literal
第一個示例嘗試將值5分配給len()調用。在這種狀況下,SyntaxError消息很是有用。它告訴你不能給函數調用賦值。
第二個和第三個示例嘗試將字符串和整數分配給文字。一樣的規則也適用於其餘文字值。一樣,回溯消息代表,當您試圖將一個值賦給一個文字時,問題就會發生。
注意:上面的示例缺乏重複的代碼行和指向回溯中的問題的插入符號(^)。當您在REPL中嘗試從文件中執行這段代碼時,您看到的異常和回溯將是不一樣的。若是這個代碼在一個文件中,那麼您將獲得重複的代碼行和指向問題的插入符號,正如您在本教程的其餘狀況中看到的那樣。
極可能你的目的不是給文字或函數調用賦值。例如,若是您不當心省略了額外的等號(=),就會發生這種狀況,這會將賦值轉換爲比較。以下所示,比較是有效的:
>>>
>>> len('hello') == 5
True
大多數狀況下,當Python告訴您正在對沒法賦值的東西進行賦值時,您首先可能須要檢查以確保語句不該該是布爾表達式。當您試圖爲Python關鍵字賦值時,也可能遇到這個問題,下一節將討論這個問題。
Python關鍵字是一組在Python中具備特殊含義的受保護的單詞。這些詞在代碼中不能用做標識符、變量或函數名。它們是語言的一部分,只能在Python容許的上下文中使用。
有三種常見的方式,你能夠錯誤地使用關鍵字:
拼錯的關鍵字
缺乏一個關鍵字
濫用關鍵字
若是您在Python代碼中拼錯了關鍵字,那麼您將獲得一個SyntaxError。例如,若是你把關鍵字拼錯了,會發生如下狀況:
>>> fro i in range(10):
File "<stdin>", line 1
fro i in range(10):
^
SyntaxError: invalid syntax
消息將讀取SyntaxError:無效語法,但這沒有多大幫助。回溯指向Python能夠檢測到錯誤的第一個地方。要修復這類錯誤,請確保全部Python關鍵字拼寫正確。
另外一個關於關鍵字的常見問題是你徹底忽略了它們:
>>>
>>> for i range(10):
File "<stdin>", line 1
for i range(10):
^
SyntaxError: invalid syntax
一樣,異常消息也不是頗有用,可是回溯確實試圖爲您指出正確的方向。若是從插入符號返回,則能夠看到for循環語法中缺乏關鍵字in。
您還可能誤用受保護的Python關鍵字。記住,關鍵字只容許在特定的狀況下使用。若是您不正確地使用它們,那麼您的Python代碼中就會出現無效的語法。一個常見的例子是在循環外使用continue或break。這在開發過程當中很容易發生,當你在實現一些東西的時候,碰巧把邏輯移出了一個循環:
>>> names = ['pam', 'jim', 'michael']
>>> if 'jim' in names:
... print('jim found')
... break
...
File "<stdin>", line 3
SyntaxError: 'break' outside loop
>>> if 'jim' in names:
... print('jim found')
... continue
...
File "<stdin>", line 3
SyntaxError: 'continue' not properly in loop
在這裏,Python很好地告訴了您到底哪裏出了問題。"'break' outside loop"和" continue' not exactly in loop"這兩個信息能夠幫助你明確地知道該怎麼作。若是這段代碼在一個文件中,那麼Python也會讓插入符號指向被誤用的關鍵字。
另外一個例子是,若是你嘗試給一個變量分配一個Python關鍵字,或者使用一個關鍵字來定義一個函數:
>>>
>>> pass = True
File "<stdin>", line 1
pass = True
^
SyntaxError: invalid syntax
>>> def pass():
File "<stdin>", line 1
def pass():
^
SyntaxError: invalid syntax
當您試圖爲pass分配一個值時,或者當您試圖定義一個名爲pass的新函數時,您將獲得一個SyntaxError並再次看到「無效語法」消息。
在Python代碼中解決這種類型的無效語法可能會稍微困難一些,由於代碼從外部看起來沒什麼問題。若是您的代碼看起來不錯,可是您仍然會獲得一個SyntaxError,那麼您能夠考慮檢查您想要使用的變量名或函數名與您正在使用的Python版本的關鍵字列表。
受保護的關鍵字列表隨着Python的每一個新版本而改變。例如,在Python 3.6中,您可使用await做爲變量名或函數名,可是在Python 3.7中,這個單詞已經被添加到關鍵字列表中。如今,若是您嘗試使用await做爲變量名或函數名,若是您的代碼是Python 3.7或更高版本,那麼這將致使SyntaxError。
另外一個例子是print,它在python2和python3中有所不一樣:
Version | print Type |
Takes A Value |
---|---|---|
Python 2 | keyword | no |
Python 3 | built-in function | yes |
print是python2中的一個關鍵字,因此你不能給它賦值。然而,在python3中,它是一個能夠賦值的內置函數。
你能夠運行如下代碼來查看關鍵字列表,不管你運行的Python版本是什麼:
import keyword
print(keyword.kwlist)yword.iskey
keyword還提供了有用的keyword.iskeyword()。若是你只是須要一個快速的方法來檢查經過變量,那麼你可使用如下一行:
>>> import keyword; keyword.iskeyword('pass')
True
這段代碼將快速告訴您要使用的標識符是不是關鍵字。
一般,Python代碼中無效語法的緣由是缺乏或不匹配的右括號、方括號或引號。在嵌套圓括號的很長行或更長的多行塊中很難發現這些。你能夠經過Python的回溯來發現不匹配或缺失的引用:
>>>
>>> message = 'don't'
File "<stdin>", line 1
message = 'don't'
^
SyntaxError: invalid syntax
這裏,回溯指向無效代碼,其中在結束單引號後有一個t'。要解決這個問題,您能夠進行如下兩種更改之一:
用反斜槓轉義單引號('don\t')
將整個字符串用雙引號括起來(「don't」)
另外一個常見的錯誤是忘記關閉字符串。對於雙引號和單引號字符串,狀況和回溯是相同的:
>>>
>>> message = "This is an unclosed string
File "<stdin>", line 1
message = "This is an unclosed string
^
SyntaxError: EOL while scanning string literal
這一次,traceback中的插入符號指向問題代碼。SyntaxError消息「在掃描字符串文字時的EOL」更具體一些,有助於肯定問題。這意味着Python解釋器在一個開放字符串關閉以前到達該行(EOL)的末尾。要解決這個問題,請使用與開始時匹配的引號關閉字符串。在本例中,將使用雙引號(")。
在f-string語句中缺乏引號也會致使Python中無效的語法:
1 # theofficefacts.py
2 ages = {
3 'pam': 24,
4 'jim': 24,
5 'michael': 43
6 }
7 print(f'Michael is {ages["michael]} years old.')
這裏,打印的f-string中對ages字典的引用缺乏關鍵引用的雙引號。獲得的回溯結果以下:
$ python theofficefacts.py
File "theofficefacts.py", line 7
print(f'Michael is {ages["michael]} years old.')
^
SyntaxError: f-string: unterminated string
Python識別問題並告訴您它存在於f-string中。消息「未終止字符串」也指出了問題所在。本例中的插入符號僅指向f-string的開頭。
當插入符號指向f-string的問題區域時,這可能沒有那麼有用,可是它確實縮小了您須要查找的範圍。在那個f字串的某個地方有一個未終止的字符串。你只須要找到在哪裏。要修復此問題,請確保全部內部f-string引號和方括號都已存在。
缺乏括號和方括號的狀況大體相同。例如,若是您從列表中刪除了右方括號,那麼Python將會發現並指出它。然而,這有一些變化。第一種是把列表中的右括號去掉:
# missing.py
def foo():
return [1, 2, 3
print(foo())
當你運行這段代碼時,你會被告知調用print()有一個問題:
$ python missing.py
File "missing.py", line 5
print(foo())
^
SyntaxError: invalid syntax
這裏發生的是Python認爲列表包含三個元素:一、2和3 print(foo())。Python使用空格從邏輯上對事物進行分組,由於從print(foo())中沒有逗號或括號分隔3,因此Python將它們集中在一塊兒做爲列表的第三個元素。
另外一種變化是在列表的最後一個元素後面添加一個逗號,同時仍然去掉右方括號:
# missing.py
def foo():
return [1, 2, 3,
print(foo())
如今你獲得了一個不一樣的回溯:
$ python missing.py
File "missing.py", line 6
^
SyntaxError: unexpected EOF while parsing
在前面的例子中,3和print(foo())被集中在一塊兒做爲一個元素,可是在這裏你能夠看到一個逗號將二者分開。如今,print(foo())的調用被添加爲列表的第四個元素,Python到達了文件的末尾,但沒有使用右括號。回溯告訴您,Python已經到達了文件(EOF)的末尾,可是它還在期待其餘內容。
在本例中,Python但願有一個右括號(]),可是重複的行和插入符號沒有多大幫助。缺乏括號和方括號是Python很難識別的。有時,您惟一能作的就是從插入符號開始,而後向後移動,直到您可以識別出缺失或錯誤的地方。
您在前面已經看到,若是將dictionary元素中的逗號去掉,可能會獲得SyntaxError。Python字典的另外一種無效語法形式是使用等號(=)來分隔鍵和值,而不是冒號:
>>>
>>> ages = {'pam'=24}
File "<stdin>", line 1
ages = {'pam'=24}
^
SyntaxError: invalid syntax
一樣,這個錯誤消息不是頗有用。可是,重複的行和插入符號很是有用!他們直接指向問題人物。
若是您將Python語法與其餘編程語言的語法混淆,那麼這種類型的問題很常見。若是將定義字典的行爲與dict()調用相混淆,也會看到這種狀況。要解決這個問題,能夠用冒號替換等號。你也能夠切換到使用dict():
>>>
>>> ages = dict(pam=24)
>>> ages
{'pam': 24}
若是dict()語法更有幫助,那麼可使用它來定義字典。
SyntaxError有兩個子類專門處理縮進問題:
IndentationError
TabError
其餘編程語言使用花括號表示代碼塊,而Python使用空格。這意味着Python指望代碼中的空白具備可預測性。若是代碼塊中有一行空格數錯誤,則會引起IndentationError:
1 # indentation.py
2 def foo():
3 for i in range(10):
4 print(i)
5 print('done')
6
7 foo()
這可能很難看到,可是第5行只縮進了2個空格。它應該與for循環語句一致,也就是4個空格。幸運的是,Python能夠很容易地發現這一點,並很快告訴您問題所在。
不過這裏也有一點模糊。打印('done')行是打算在for循環以後仍是在for循環塊內部?當你運行上述代碼,你會看到如下錯誤:
$ python indentation.py
File "indentation.py", line 5
print('done')
^
IndentationError: unindent does not match any outer indentation level
雖然回溯看起來很像SyntaxError回溯,但它其實是一個IndentationError。錯誤消息也很是有用。它告訴您行的縮進級別與其餘任何縮進級別不匹配。換句話說,print('done')是縮進2個空格的,可是Python找不到任何其餘匹配這種縮進級別的代碼行。您能夠經過確保代碼行符合預期的縮進級別來快速修復此問題。
SyntaxError的另外一種類型是TabError,只要有一行包含製表符或空格做爲縮進,而文件的其他部分包含製表符或空格,就會看到TabError。這可能會隱藏起來,直到Python指出來!
若是製表符的大小與每一個縮進級別中的空格數相同,那麼看起來全部的行彷佛都在同一級別上。然而,若是一行用空格縮進,另外一行用製表符縮進,那麼Python會指出這是一個問題:
1 # indentation.py
2 def foo():
3 for i in range(10):
4 print(i)
5 print('done')
6
7 foo()
在這裏,第5行是用製表符縮進的,而不是用4個空格。根據您的系統設置,這個代碼塊在您看來多是完美的,也多是徹底錯誤的。
可是,Python會當即注意到這個問題。但在你運行代碼,看看Python會告訴你什麼是錯誤的以前,它可能會對你有幫助,看看在不一樣的標籤寬度設置下的代碼是什麼樣子的一個例子:
$ tabs 4 # Sets the shell tab width to 4 spaces
$ cat -n indentation.py
1 # indentation.py
2 def foo():
3 for i in range(10)
4 print(i)
5 print('done')
6
7 foo()
$ tabs 8 # Sets the shell tab width to 8 spaces (standard)
$ cat -n indentation.py
1 # indentation.py
2 def foo():
3 for i in range(10)
4 print(i)
5 print('done')
6
7 foo()
$ tabs 3 # Sets the shell tab width to 3 spaces
$ cat -n indentation.py
1 # indentation.py
2 def foo():
3 for i in range(10)
4 print(i)
5 print('done')
6
7 foo()
請注意上面三個示例之間的顯示差別。大多數代碼爲每一個縮進級別使用4個空格,可是第5行在全部3個示例中都使用單個選項卡。標籤寬度的變化,基於標籤寬度的設置:
若是製表符寬度爲4,那麼print語句看起來就像是在for循環以外。控制檯將在循環結束時打印「done」。
若是製表符寬度是8,這是不少系統的標準,那麼print語句看起來就像是在for循環中。控制檯會在每一個數字以後打印「done」。
若是製表符寬度爲3,那麼print語句看起來就不合適。在本例中,第5行與任何縮進級別不匹配。
當你運行代碼,你會獲得如下錯誤和回溯:
$ python indentation.py
File "indentation.py", line 5
print('done')
^
TabError: inconsistent use of tabs and spaces in indentation
請注意TabError而不是一般的SyntaxError。Python指出問題所在,並給出有用的錯誤消息。它清楚地告訴您,在同一個文件中,製表符和空格用於縮進。
解決這個問題的方法是讓同一Python代碼文件中的全部行都使用製表符或空格,而不是同時使用製表符和空格。對於上面的代碼塊,修復方法是刪除選項卡並用4個空格替換它,這將在For循環完成後打印「done」。
在定義或調用函數時,可能會遇到Python中無效的語法。例如,若是在函數定義的末尾使用分號而不是冒號,就會看到SyntaxError:
>>>
>>> def fun();
File "<stdin>", line 1
def fun();
^
SyntaxError: invalid syntax
這裏的回溯很是有用,插入符號直接指向問題字符。您能夠經過將分號替換爲冒號來清除Python中的這種無效語法。
此外,函數定義和函數調用中的關鍵字參數的順序必須正確。關鍵字參數老是跟在位置參數以後。若是不使用此順序,將致使SyntaxError:
>>>
>>> def fun(a, b):
... print(a, b)
...
>>> fun(a=1, 2)
File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument
這裏,一樣,錯誤消息很是有助於準確地告訴您這行代碼的錯誤之處。
有時候,在一個Python版本中工做得很好的代碼會在一個新版本中中斷。這是因爲語言語法的官方變化。這方面最著名的例子是print語句,它從python2中的一個關鍵字變成了python3中的一個內置函數:
>>>
>>> # Valid Python 2 syntax that fails in Python 3
>>> print 'hello'
File "<stdin>", line 1
print 'hello'
^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print('hello')?
這是SyntaxError提供的錯誤消息所展現的示例之一!它不只告訴您在print調用中缺乏圓括號,並且還提供了正確的代碼來幫助您修復語句。
您可能會遇到的另外一個問題是,當您閱讀或學習在較新版本的Python中有效的語法,但在您正在編寫的版本中無效的語法時。f-string語法就是一個例子,在3.6以前的Python版本中是不存在的:
>>>
>>> # Any version of python before 3.6 including 2.7
>>> w ='world'
>>> print(f'hello, {w}')
File "<stdin>", line 1
print(f'hello, {w}')
^
SyntaxError: invalid syntax
在3.6以前的Python版本中,解釋器對f-string語法一無所知,只會提供一個通用的「無效語法」消息。在本例中,問題是代碼看起來很是好,可是它是在較老版本的Python中運行的。若是有疑問,請再次檢查正在運行的Python版本!
Python語法在不斷髮展,Python 3.8中引入了一些很酷的新特性:
Walrus操做符(賦值表達式)
用於調試的F-string語法
Positional-only參數
若是您想嘗試這些新特性,那麼您須要確保您是在Python 3.8環境中工做。不然,您將獲得一個SyntaxError。
Python 3.8還提供了新的SyntaxWarning。在語法有效但看起來仍然可疑的狀況下,您將看到此警告。例如,若是列表中的兩個元組之間缺乏逗號。這在3.8以前的Python版本中是有效的語法,可是代碼會產生一個類型錯誤,由於tuple是不可調用的:
>>>
>>> [(1,2)(2,3)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object is not callable
這個類型錯誤意味着您不能像調用函數那樣調用元組,這是Python解釋器認爲您正在作的。
在Python 3.8中,這段代碼仍然會引起類型錯誤,可是如今您還會看到一個SyntaxWarning,它指示如何着手修復問題:
>>>
>>> [(1,2)(2,3)]
<stdin>:1: SyntaxWarning: 'tuple' object is not callable; perhaps you missed a comma?
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object is not callable
新SyntaxWarning附帶的有用信息甚至提供了一個提示(「也許您漏掉了一個逗號?」),以便爲您指出正確的方向!
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
在本教程中,您已經看到了SyntaxError回溯所提供的信息。您還看到了Python中許多常見的無效語法示例,以及這些問題的解決方案。這不只會加快你的工做流程,並且還會使你成爲一個更有幫助的代碼審查者!
在編寫代碼時,請嘗試使用可以理解Python語法並提供反饋的IDE。若是您將本教程中的許多無效Python代碼示例放到一個良好的IDE中,那麼它們應該在您執行代碼以前突出顯示問題行。
在學習Python時得到一個SyntaxError可能會使人沮喪,可是如今您知道了如何理解回溯消息以及在Python中可能遇到的無效語法形式。下一次出現SyntaxError時,您就能夠更好地快速修復這個問題了!
原文請點擊左下角閱讀原文
·END·
機器學習·數據分析
本文分享自微信公衆號 - Python學會(gh_39aead19f756)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。