前言
Python以其簡單的文法而聞名。然而,當您第一次學習Python時,或者當您具有另一種程式設計語言的堅實背景時,您可能會遇到一些Python不允許的事情。如果您在嘗試運作Python代碼時收到過SyntaxError錯誤,那麼本指南可以幫助您。在本教程中,您将看到Python中常見的無效文法示例,并學習如何解決這個問題。
在本教程結束時,您将能夠:
- 識别Python中的無效文法
- 了解SyntaxError回溯
- 解析無效文法或完全阻止它

Python中的無效文法
當您運作Python代碼時,解釋器将首先解析它,将其轉換成Python位元組碼,然後執行。解釋器将在程式執行的第一階段(也稱為解析階段)中發現Python中的任何無效文法。如果解釋器不能成功地解析您的Python代碼,那麼這意味着您在代碼的某個地方使用了無效的文法。解釋器将嘗試向您顯示錯誤發生的位置。
當您第一次學習Python時,得到一個SyntaxError可能會令人沮喪。Python将嘗試幫助您确定無效文法在代碼中的位置,但是它提供的回溯可能會讓您感到有些困惑。有時,它所指向的代碼是完全正确的。
您不能像處理其他異常一樣處理Python中的無效文法。即使您嘗試将try和except塊封裝到帶有無效文法的代碼中,您仍然會看到解釋器抛出一個SyntaxError。
SyntaxError異常和回溯
當解釋器在Python代碼中遇到無效文法時,它将抛出一個SyntaxError異常,并提供一個帶有一些有用資訊的回溯,以幫助您調試錯誤。下面是一些Python中包含無效文法的代碼:
# theofficefacts.py ages = { 'pam': 24, 'jim': 24 'michael': 43 } print(f'Michael is {ages["michael"]} years old.')
您可以在第4行字典的文字中看到無效的文法。第二個詞條“jim”漏掉了一個逗号。如果你試着按原樣運作這段代碼,你會得到以下回溯結果:
$ python theofficefacts.pyFile "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。當代碼在同一檔案中同時使用制表符和空格時,将引發一個制表符錯誤。在後面的小節中,您将進一步了解這些異常。
常見的文法問題
當您第一次遇到SyntaxError時,了解為什麼會出現問題以及如何修複Python代碼中的無效文法是很有幫助的。在下面的小節中,您将看到可能引發SyntaxError的一些更常見的原因,以及如何修複它們。
1.誤用指派運算符(=)
在Python中有幾種情況下,您不能對對象進行指派。一些例子是配置設定文字和函數調用。在下面的代碼塊中,您可以看到一些嘗試這樣做的示例和由此産生的SyntaxError回溯:
>>> len('hello') = 5 File "", line 1SyntaxError: can't assign to function call>>> 'foo' = 1 File "", line 1SyntaxError: can't assign to literal>>> 1 = 'foo' File "", line 1SyntaxError: can't assign to literal
第一個示例嘗試将值5配置設定給len()調用。在這種情況下,SyntaxError消息非常有用。它告訴你不能給函數調用指派。
第二個和第三個示例嘗試将字元串和整數配置設定給文字。同樣的規則也适用于其他文字值。同樣,回溯消息表明,當您試圖将一個值賦給一個文字時,問題就會發生。
注意:上面的示例缺少重複的代碼行和指向回溯中的問題的插入符号(^)。當您在REPL中嘗試從檔案中執行這段代碼時,您看到的異常和回溯将是不同的。如果這個代碼在一個檔案中,那麼您将得到重複的代碼行和指向問題的插入符号,正如您在本教程的其他情況中看到的那樣。
很可能你的目的不是給文字或函數調用指派。例如,如果您不小心省略了額外的等号(=),就會發生這種情況,這會将指派轉換為比較。如下所示,比較是有效的:
>>> len('hello') == 5True
大多數情況下,當Python告訴您正在對無法指派的東西進行指派時,您首先可能需要檢查以確定語句不應該是布爾表達式。當您試圖為Python關鍵字指派時,也可能遇到這個問題,下一節将讨論這個問題。
2.拼寫錯誤、缺少或誤用Python關鍵字
Python關鍵字是一組在Python中具有特殊含義的受保護的單詞。這些詞在代碼中不能用作辨別符、變量或函數名。它們是語言的一部分,隻能在Python允許的上下文中使用。
有三種常見的方式,你可以錯誤地使用關鍵字:
- 拼錯的關鍵字
- 缺少一個關鍵字
- 濫用關鍵字
如果您在Python代碼中拼錯了關鍵字,那麼您将得到一個SyntaxError。例如,如果你把關鍵字拼錯了,會發生以下情況:
>>> fro i in range(10): File "", line 1 fro i in range(10): ^SyntaxError: invalid syntax
消息将讀取SyntaxError:無效文法,但這沒有多大幫助。回溯指向Python可以檢測到錯誤的第一個地方。要修複這類錯誤,請確定所有Python關鍵字拼寫正确。
另一個關于關鍵字的常見問題是你完全忽略了它們:
>>> for i range(10): File "", 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 "", line 3SyntaxError: 'break' outside loop>>> if 'jim' in names:... print('jim found')... continue... File "", line 3SyntaxError: 'continue' not properly in loop
在這裡,Python很好地告訴了您到底哪裡出了問題。"'break' outside loop"和" continue' not exactly in loop"這兩個資訊可以幫助你明确地知道該怎麼做。如果這段代碼在一個檔案中,那麼Python也會讓插入符号指向被誤用的關鍵字。
另一個例子是,如果你嘗試給一個變量配置設定一個Python關鍵字,或者使用一個關鍵字來定義一個函數:
>>> pass = TrueFile "", line 1 pass = True ^SyntaxError: invalid syntax>>> def pass(): File "", 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中有所不同:
print是python2中的一個關鍵字,是以你不能給它指派。然而,在python3中,它是一個可以指派的内置函數。
你可以運作以下代碼來檢視關鍵字清單,無論你運作的Python版本是什麼:
import keywordprint(keyword.kwlist)yword.iskey
keyword還提供了有用的keyword.iskeyword()。如果你隻是需要一個快速的方法來檢查通過變量,那麼你可以使用以下一行:
>>> import keyword; keyword.iskeyword('pass')True
這段代碼将快速告訴您要使用的辨別符是否是關鍵字。
3.缺少括号、方括号和引号
通常,Python代碼中無效文法的原因是缺少或不比對的右括号、方括号或引号。在嵌套圓括号的很長行或更長的多行塊中很難發現這些。你可以通過Python的回溯來發現不比對或缺失的引用:
>>> message = 'don't' File "", line 1 message = 'don't' ^SyntaxError: invalid syntax
這裡,回溯指向無效代碼,其中在結束單引号後有一個t'。要解決這個問題,您可以進行以下兩種更改之一:
- 用反斜杠轉義單引号('don')
- 将整個字元串用雙引号括起來(“don't”)
另一個常見的錯誤是忘記關閉字元串。對于雙引号和單引号字元串,情況和回溯是相同的:
>>> message = "This is an unclosed string File "", line 1 message = "This is an unclosed string ^SyntaxError: EOL while scanning string literal
這一次,traceback中的插入符号指向問題代碼。SyntaxError消息“在掃描字元串文字時的EOL”更具體一些,有助于确定問題。這意味着Python解釋器在一個開放字元串關閉之前到達該行(EOL)的末尾。要解決這個問題,請使用與開始時比對的引号關閉字元串。在本例中,将使用雙引号(")。
在f-string語句中缺少引号也會導緻Python中無效的文法:
# theofficefacts.py ages = { 'pam': 24, 'jim': 24, 'michael': 43 } 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.pydef foo(): return [1, 2, 3print(foo())
當你運作這段代碼時,你會被告知調用print()有一個問題:
$ python missing.py File "missing.py", line 5 print(foo()) ^SyntaxError: invalid syntax
這裡發生的是Python認為清單包含三個元素:1、2和3 print(foo())。Python使用空格從邏輯上對事物進行分組,因為從print(foo())中沒有逗号或括号分隔3,是以Python将它們集中在一起作為清單的第三個元素。
另一種變化是在清單的最後一個元素後面添加一個逗号,同時仍然去掉右方括号:
# missing.pydef 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很難識别的。有時,您唯一能做的就是從插入符号開始,然後向後移動,直到您能夠識别出缺失或錯誤的地方。
更多内容,請通路原文:
https://realpython.com/invalid-syntax-python/#common-syntax-problems
結論
在本教程中,您已經看到了SyntaxError回溯所提供的資訊。您還看到了Python中許多常見的無效文法示例,以及這些問題的解決方案。這不僅會加快你的工作流程,而且還會使你成為一個更有幫助的代碼審查者!
在編寫代碼時,請嘗試使用能夠了解Python文法并提供回報的IDE。如果您将本教程中的許多無效Python代碼示例放到一個良好的IDE中,那麼它們應該在您執行代碼之前突出顯示問題行。
在學習Python時獲得一個SyntaxError可能會令人沮喪,但是現在您知道了如何了解回溯消息以及在Python中可能遇到的無效文法形式。下一次出現SyntaxError時,您就可以更好地快速修複這個問題了!