Home >> Blog >> Python 中的無效語法 invalid syntax:SyntaxError 的常見原因

Python 中的無效語法 invalid syntax:SyntaxError 的常見原因

Python以其簡單的語法而聞名。然而,當你因為接觸SEO search engine optimization這行業而必須啟動你的第一次學習 Python 或者當你以另一種編寫程式語言的紮實背景來到 Python 時,你可能會遇到一些 Python 不允許的事情。SyntaxError如果您在嘗試運行 Python 程式碼時曾經收到過提示,那麼本指南可以為您提供幫助。在本教學中,您將看到 Python 中無效語法的常見示例,並了解如何解決該問題。

在本教學結束時,您將能夠:

  • 識別Python 中的無效語法
  • 理解SyntaxError回溯
  • 解決無效語法或完全阻止它

免費獎勵: 5 Thoughts On Python Mastery,這是一門面向 Python 開發人員的免費課程,向您展示了將 Python 技能提升到新水平所需的路線圖和心態。

Python中的無效語法

當您運行 Python 程式碼時,解釋器interpreter 將首先對其進行解析,將其轉換為 Python 字節碼,然後執行。解釋器將在程序執行的第一階段(也稱為解析階段)中發現 Python 中的任何無效語法。如果解釋器無法成功解析您的 Python 程式碼,那麼這意味著您在程式碼中的某處使用了無效語法。解釋器將嘗試向您顯示錯誤發生的位置。

當你第一次學習 Python 時,獲得一個SyntaxError. Python 將嘗試幫助您確定無效語法在程式碼中的位置,但它提供的回溯可能有點令人困惑。有時,它指向的程式碼非常好。

注意:如果您的程式碼在語法上是正確的,那麼您可能會引發其他不屬於SyntaxError. 要了解有關 Python 的其他異常以及如何處理它們的更多信息,請查看Python Exceptions: An Introduction。

您不能像處理其他異常一樣處理 Python 中的無效語法。即使您嘗試使用無效語法包裝 atry並except阻塞程式碼,您仍然會看到解釋器引發 a 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.py
File "theofficefacts.py", line 5
'michael': 43
^
SyntaxError: invalid syntax

請注意,回溯消息將錯誤定位在第 5 行,而不是第 4 行。Python 解釋器試圖指出無效語法的位置。但是,它只能真正指出它首先註意到問題的地方。當您獲得SyntaxError回溯並且回溯指向的程式碼看起來不錯時,您將希望開始向後移動程式碼,直到您可以確定出了什麼問題。

在上面的示例中,省略逗號沒有問題,具體取決於它後面的內容。例如,'michael'第 5 行後面缺少逗號是沒有問題的。但是一旦解釋器遇到沒有意義的東西,它只能將您指向它發現的第一個無法理解的東西。

注意:本教學假設您了解 Python 的tracebacks的基礎知識。要了解有關 Python 回溯以及如何閱讀它們的更多信息,請查看了解 Python 回溯和充分利用 Python 回溯。

回溯的一些元素SyntaxError可以幫助您確定程式碼中無效語法的位置:

  • 遇到無效語法的文件名
  • 遇到問題的行號和重現的程式碼行
  • ^複製程式碼下方的行上的插入符號 ( ) ,它向您顯示程式碼中存在問題的點
  • 異常類型之後的錯誤信息SyntaxError,它可以提供信息來幫助您確定問題。

在上面的示例中,給出的文件名是theofficefacts.py,行號是 5,插入符號指向字典鍵的右引號michael。回溯可能不會指向真正的SyntaxError問題,但它會指向解釋器無法理解語法的第一個地方。

您可能會看到 Python 引發的另外兩個例外。這些等價於SyntaxError但具有不同的名稱:

  1. IndentationError
  2. TabError

這些異常都繼承自SyntaxError類,但它們是涉及縮進的特殊情況。IndentationError當您的程式碼的縮進級別不匹配時會引發An 。TabError當您的程式碼在同一文件中同時使用製表符和空格時,會引發A。您將在後面的部分中仔細研究這些異常。

常見語法問題

當您SyntaxError第一次遇到 a 時,了解為什麼會出現問題以及您可以採取哪些措施來修復 Python 程式碼中的無效語法會很有幫助。在下面的部分中,您將看到一些SyntaxError可能引發 a 的更常見原因以及如何解決這些問題。

誤用賦值運算符 ( =)

在 Python 中有幾種情況,您無法對對象進行賦值。一些示例是分配給文字和函數調用。在下面的程式碼塊中,您可以看到一些嘗試執行此操作的示例以及生成的SyntaxError回溯:

>>> len('hello') = 5
File "", line 1
SyntaxError: can't assign to function call

>>> 'foo' = 1
File "", line 1
SyntaxError: can't assign to literal

>>> 1 = 'foo'
File "", line 1
SyntaxError: can't assign to literal

第一個示例嘗試將值分配給5調用len()。在這種情況下,該SyntaxError消息非常有用。它告訴您不能為函數調用賦值。

第二個和第三個示例嘗試將字符串和整數分配給文字。同樣的規則也適用於其他文字值。回溯消息再次表明,當您嘗試將值分配給文字時會出現問題。

注意:上面的示例缺少重複的程式碼行和指向回溯中問題的插入符號 ( )。當您在 REPL 中與嘗試從文件執行此程式碼時,您看到的異常和回溯會有所不同。如果此程式碼在一個文件中,那麼您會看到重複的程式碼行和指向問題的插入符號,正如您在本教學中的其他情況中看到的那樣。

您的意圖可能不是為文字或函數調用分配值。例如,如果您不小心遺漏了額外的等號 ( =),就會發生這種情況,這會將賦值變成比較。如下所示,比較是有效的:

>>> len('hello') == 5
True

大多數時候,當 Python 告訴您正在對無法分配的東西進行分配時,您首先可能需要檢查以確保該語句不應該是布爾表達式。當您嘗試將值分配給Python 關鍵字時,您也可能會遇到此問題,您將在下一節中介紹。

拼寫錯誤、遺漏或誤用 Python 關鍵字

Python 關鍵字是一組在 Python 中具有特殊含義的受保護詞。這些詞不能用作程式碼中的標識符、變量或函數名。它們是語言的一部分,只能在 Python 允許的上下文中使用。

錯誤使用關鍵字的常見方式有以下三種:

  1. 拼錯關鍵字
  2. 缺少關鍵字
  3. 濫用關鍵字

如果你在 Python 程式碼中拼錯了一個關鍵字,那麼你會得到一個SyntaxError. 例如,如果您拼寫for錯誤的關鍵字會發生以下情況:

>>> fro i in range(10):
File "", line 1
fro i in range(10):
^
SyntaxError: invalid syntax

該消息顯示為SyntaxError: invalid syntax,但這不是很有幫助。回溯指向 Python 可以檢測到錯誤的第一個位置。要修復此類錯誤,請確保所有 Python 關鍵字拼寫正確。

關鍵字的另一個常見問題是當您完全錯過它們時:

>>> for i range(10):
File "", line 1
for i range(10):
^
SyntaxError: invalid syntax

再一次,異常消息沒有那麼有用,但回溯確實試圖為您指明正確的方向。如果從插入符號移回,則可以看到循環語法in中缺少關鍵字。for

您還可以濫用受保護的 Python 關鍵字。請記住,關鍵字只允許在特定情況下使用。如果你不正確地使用它們,那麼你的 Python 程式碼中就會出現無效的語法。一個常見的例子是循環的使用continue或break循環之外。這很容易在開發過程中發生,當您實現事物並碰巧將邏輯移出循環時:

>>> names = ['pam', 'jim', 'michael']
>>> if 'jim' in names:
... print('jim found')
... break
...
File "", line 3
SyntaxError: 'break' outside loop

>>> if 'jim' in names:
... print('jim found')
... continue
...
File "", line 3
SyntaxError: 'continue' not properly in loop

在這裡,Python 很好地告訴你到底哪裡出了問題。這些消息"'break' outside loop"並"'continue' not properly in loop"幫助您確切地確定要做什麼。如果這段程式碼在一個文件中,那麼 Python 也會有插入符號指向被誤用的關鍵字。

另一個示例是,如果您嘗試將 Python 關鍵字分配給變量或使用關鍵字來定義函數:

>>> pass = True
File "", line 1
pass = True
^
SyntaxError: invalid syntax

>>> def pass():
File "", line 1
def pass():
^
SyntaxError: invalid syntax

當您嘗試為 賦值pass或嘗試定義一個名為 的新函數時pass,您將獲得 aSyntaxError並再次看到該"invalid syntax"消息。

在 Python 程式碼中解決這種類型的無效語法可能會有點困難,因為從外部看起來程式碼很好。如果您的程式碼看起來不錯,但您仍然得到一個SyntaxError,那麼您可能會考慮根據您正在使用的 Python 版本的關鍵字列表檢查要使用的變量名或函數名。

受保護的關鍵字列表隨著 Python 的每個新版本而變化。例如,在 Python 3.6 中,您可以將await其用作變量名或函數名,但從 Python 3.7 開始,該詞已添加到關鍵字列表中。現在,如果您嘗試將其用作變量或函數名稱,如果您的程式碼適用於 Python 3.7 或更高版本await,這將導致錯誤。SyntaxError

另一個例子是print,它在 Python 2 和 Python 3 中有所不同:

print是 Python 2 中的一個關鍵字,所以你不能給它賦值。然而,在 Python 3 中,它是一個可以賦值的內置函數。

您可以運行以下程式碼來查看您正在運行的任何 Python 版本中的關鍵字列表:

import keyword
print(keyword.kwlist)

keyword也提供了有用的keyword.iskeyword()。如果您只需要一種快速檢查pass變量的方法,那麼您可以使用以下單行:

>>> import keyword; keyword.iskeyword('pass')
True

此程式碼將快速告訴您您嘗試使用的標識符是否是關鍵字。

缺少括號、方括號和引號

通常,Python 程式碼中語法無效的原因是右括號、括號或引號丟失或不匹配。這些很難在很長的嵌套括號行或更長的多行塊中發現。借助 Python 的回溯,您可以發現不匹配或缺失的引號:

>>> message = 'don't'
File "", line 1
message = 'don't'
^
SyntaxError: invalid syntax

在這裡,回溯指向在t'單引號後有 a 的無效程式碼。要解決此問題,您可以進行以下兩項更改之一:

  1. 用反斜杠 ( 'don\'t')轉義單引號
  2. 將整個字符串用雙引號括起來 ( "don't")

另一個常見的錯誤是忘記關閉字符串。對於雙引號和單引號字符串,情況和回溯是相同的:

>>> message = "This is an unclosed string
File "", line 1
message = "This is an unclosed string
^
SyntaxError: EOL while scanning string literal

這一次,回溯中的插入符號指向問題程式碼。SyntaxError消息更具體"EOL while scanning string literal",有助於確定問題。這意味著 Python 解釋器在打開的字符串關閉之前到達了行尾 (EOL)。要解決此問題,請使用與您用來啟動它的引號相匹配的引號來關閉字符串。在這種情況下,這將是一個雙引號 ( ")。

f-string中的語句中缺少引號也可能導致 Python 中的語法無效:

# theofficefacts.py
ages = {
'pam': 24,
'jim': 24,
'michael': 43
}
print(f'Michael is {ages["michael]} years old.')

在這裡,ages打印的 f 字符串中對字典的引用缺少鍵引用中的右雙引號。生成的回溯如下:

$ python theofficefacts.py
File "theofficefacts.py", line 7
print(f'Michael is {ages["michael]} years old.')
^
SyntaxError: f-string: unterminated string

Python 識別出問題並告訴您它存在於 f 字符串中。該消息"unterminated string"還指出問題所在。在這種情況下,插入符號僅指向 f 字符串的開頭。

這可能不像插入符號指向 f 字符串的問題區域那樣有用,但它確實縮小了您需要查看的位置。在那個 f 字符串的某個地方有一個未終止的字符串。你只需要找出在哪裡。要解決此問題,請確保存在所有內部 f 字符串引號和括號。

缺少括號和括號的情況基本相同。例如,如果您從list 中省略了右方括號,那麼 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 認為列表包含三個元素:1、2和3 print(foo())。Python 使用空格對事物進行邏輯分組,並且因為沒有逗號或括號與 分隔3,print(foo())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 很難識別缺少括號和方括號。有時,您唯一能做的就是從插入符號開始並向後移動,直到您可以確定丟失或錯誤的內容。

錯誤的字典語法

您之前SyntaxError看到,如果您將逗號從字典元素中去掉,您可能會得到 a 。Python 字典的另一種無效語法形式是使用等號 ( =) 來分隔鍵和值,而不是冒號:

>>> ages = {'pam'=24}
File "", line 1
ages = {'pam'=24}
^
SyntaxError: invalid syntax

再一次,此錯誤消息不是很有幫助。然而,重複的行和插入符號非常有幫助!他們正直指問題人物。

如果您將 Python 語法與其他編寫程式語言的語法混淆,則此類問題很常見。如果您將定義字典的行為與dict()調用混淆,您也會看到這一點。要解決此問題,您可以用冒號替換等號。您也可以切換到使用dict():

>>> ages = dict(pam=24)
>>> ages
{'pam': 24}

dict()如果該語法更有幫助,您可以使用它來定義字典。

使用錯誤的縮進

有兩個子類SyntaxError專門處理縮進問題:

  1. IndentationError
  2. TabError

雖然其他編寫程式語言使用花括號來表示程式碼塊,但 Python 使用空格。這意味著 Python 期望程式碼中的空白行為可預測。IndentationError如果程式碼塊中有一行空格數錯誤,它將引發:

# indentation.py
def foo():
for i in range(10):
print(i)
print('done')

foo()

這可能很難看出,但第 5 行僅縮進了 2 個空格。它應該與for循環語句一致,即超過 4 個空格。值得慶幸的是,Python 可以輕鬆發現這一點,並會很快告訴您問題所在。

不過,這裡也有一些模棱兩可的地方。該print('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,只要有一行包含製表符或空格用於縮進,您就會看到它,而文件的其餘部分包含另一個。這可能會隱藏起來,直到 Python 向您指出!

如果您的製表符大小與每個縮進級別中的空格數相同,那麼看起來所有行都處於同一級別。但是,如果一行使用空格縮進,另一行使用製表符縮進,那麼 Python 會指出這是一個問題:

# indentation.py
def foo():
for i in range(10):
print(i)
print('done')

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 行在所有三個示例中都使用了一個製表符。選項卡的寬度會根據選項卡寬度設置發生變化:

  • 如果選項卡寬度為 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 個空格,這將'done'在for循環完成後打印。

定義和調用函數

在定義或調用函數時,您可能會在 Python 中遇到無效語法。例如,SyntaxError如果在函數定義的末尾使用分號而不是冒號,您會看到:

>>> def fun();
File "", line 1
def fun();
^
SyntaxError: invalid syntax

這裡的回溯非常有幫助,插入符號指向問題字符。您可以通過將分號換成冒號來清除 Python 中的這種無效語法。

此外,函數定義和函數調用中的關鍵字參數需要以正確的順序排列。關鍵字參數總是在位置參數之後。不使用此排序將導致SyntaxError:

>>> def fun(a, b):
... print(a, b)
...
>>> fun(a=1, 2)
File "", line 1
SyntaxError: positional argument follows keyword argument

在這裡,錯誤消息再次非常有助於準確地告訴您該行出了什麼問題。

更改 Python 版本

有時,在一個 Python 版本中運行良好的程式碼會在新版本中中斷。這是由於語言語法的官方更改。最著名的例子是print語句,它從 Python 2 中的關鍵字變成了 Python 3 中的內置函數:

T>>> # Valid Python 2 syntax that fails in Python 3
>>> print 'hello'
File "", line 1
print 'hello'
^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print('hello')?

這是與SyntaxError閃耀一起提供的錯誤消息的示例之一!它不僅告訴您print調用中缺少括號,而且還提供了正確的程式碼來幫助您修復語句。

您可能會遇到的另一個問題是,當您閱讀或學習在較新版本的 Python 中是有效語法但在您正在編寫的版本中無效的語法時。這方面的一個例子是f-string語法,這在 3.6 之前的 Python 版本中不存在:

T>>> # Any version of python before 3.6 including 2.7
>>> w ='world'
>>> print(f'hello, {w}')
File "", line 1
print(f'hello, {w}')
^
SyntaxError: invalid syntax

在 3.6 之前的 Python 版本中,解釋器對 f-string 語法一無所知,只會提供通用"invalid syntax"消息。在這種情況下,問題在於程式碼看起來非常好,但它是使用舊版本的 Python 運行的。如有疑問,請仔細檢查您正在運行的 Python 版本!

Python 語法不斷發展,Python 3.8中引入了一些很酷的新特性:

  • 海象運算符(賦值表達式)
  • 用於調試的 F 字符串語法
  • 僅位置參數

如果您想嘗試其中的一些新功能,那麼您需要確保您在 Python 3.8 環境中工作。否則,你會得到一個SyntaxError.

Python 3.8 還提供了新的SyntaxWarning. 在語法有效但看起來仍然可疑的情況下,您會看到此警告。例如,如果您在列表中的兩個元組之間缺少逗號。這在 3.8 之前的 Python 版本中是有效的語法,但程式碼會引發 aTypeError因為元組不可調用:

>>> [(1,2)(2,3)]
Traceback (most recent call last):
File "", line 1, in
TypeError: 'tuple' object is not callable

這TypeError意味著您不能像函數一樣調用元組,這是 Python 解釋器認為您正在做的事情。

在 Python 3.8 中,此程式碼仍會引發TypeError,但現在您還會看到SyntaxWarning指示如何著手解決問題:

>>> [(1,2)(2,3)]
:1: SyntaxWarning: 'tuple' object is not callable; perhaps you missed a comma?
Traceback (most recent call last):
File "", line 1, in
TypeError: 'tuple' object is not callable


新產品隨附的有用信息SyntaxWarning甚至提供了提示 ( "perhaps you missed a comma?") 來為您指明正確的方向!

結論

在本教學中,您已經了解了SyntaxError回溯為您提供了哪些信息。您還看到了許多 Python 中無效語法的常見示例以及這些問題的解決方案。這不僅會加快您的工作流程,還會讓您成為更有幫助的程式碼審查者!

在編寫程式碼時,請嘗試使用理解 Python 語法並提供反饋的IDE 。如果您將本教學中的許多無效 Python 程式碼示例放入一個好的 IDE,那麼它們應該在您執行程式碼之前突出顯示問題行。

花點時間SyntaxError學習 Python 可能會令人沮喪,但現在您知道如何理解回溯消息以及您可能遇到的 Python 中哪些形式的無效語法。下次你得到一個SyntaxError,你會更好地快速解決問題!