8.1. Syntax Errors
Syntax errors, also known as parsing errors, are perhaps the most common kind of complaint you get while you are still learning Python:
語法錯誤,同樣也稱為句法分析錯誤,當你正在學習Python的過程中,可能是你抱怨最多的問題。
>>> while True print('Hello world') File "<stdin>", line 1, in ? while True print('Hello world') ^ SyntaxError: invalid syntax
The parser repeats the offending line and displays a little ‘arrow’ pointing at the earliest point in the line where the error was detected. The error is caused by (or at least detected at) the token preceding the arrow: in the example, the error is detected at the function print(), since a colon (':') is missing before it. File name and line number are printed so you know where to look in case the input came from a script.
語法分析器會重寫出錯的語句,並用一個向上的箭頭指向錯誤被檢查到的地方。在這個例子中,錯誤是由於在print()函數前缺少冒號:造成的。文件名和行號也打印出來,因此你可以將錯誤輸出到一個腳本中,方便定位問題。
8.2. Exceptions
Even if a statement or expression is syntactically correct, it may cause an error when an attempt is made to execute it. Errors detected during execution are called exceptions and are not unconditionally fatal: you will soon learn how to handle them in Python programs. Most exceptions are not handled by programs, however, and result in error messages as shown here:
即使語句或者表達式在語法上是正確的,執行的時候也可能出錯。在程序運行期間出現致命錯誤或者程序調用異常時會進行錯誤檢測:你將會學習到在Python程序中如何處理它們。許多異常程序是不會手動處理的:
>>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in ? ZeroDivisionError: division by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in ? NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: Can't convert 'int' object to str implicitly
The last line of the error message indicates what happened. Exceptions come in different types, and the type is printed as part of the message: the types in the example are ZeroDivisionError, NameError and TypeError. The string printed as the exception type is the name of the built-in exception that occurred. This is true for all built-in exceptions, but need not be true for user-defined exceptions (although it is a useful convention). Standard exception names are built-in identifiers (not reserved keywords).
錯誤消息的最后一行顯示發生了什么情況。異常有很多類型,並作為消息的一部分打印出來:在這個例子中,異常類型有ZeroDivisionError,NameError和TypeError。這些異常名字是Python內置的。用戶也可以自定義異常類型。
The rest of the line provides detail based on the type of exception and what caused it.
錯誤消息的其他部分提供了引發異常的細節情況。
The preceding part of the error message shows the context where the exception happened, in the form of a stack traceback. In general it contains a stack traceback listing source lines; however, it will not display lines read from standard input.
錯誤消息的前面應該會指明異常發生的上下文,例如指出堆棧信息。一般情況下,它應該包含源碼級別的堆棧信息;但是上面的例子是從標准輸入設備中讀取的代碼,因此沒有顯示堆棧信息。
8.3. Handling Exceptions
It is possible to write programs that handle selected exceptions. Look at the following example, which asks the user for input until a valid integer has been entered, but allows the user to interrupt the program (using Control-C or whatever the operating system supports); note that a user-generated interruption is signalled by raising the KeyboardInterrupt exception.
當然,我們可以在程序中選擇性的處理需要處理的異常。看下面的例子,請求用戶的輸入直到輸入合法的整數值,但是也允許用戶打斷程序的運行(使用Control-C或者Control-Z);注意,用戶觸發的打斷將會觸發KeyboardInterrupt異常。
>>> while True: ... try: ... x = int(input("Please enter a number: ")) ... break ... except ValueError: ... print("Oops! That was no valid number. Try again...") ...
The try statement works as follows.
try語句工作機制:
- First, the try clause (the statement(s) between the try and except keywords) is executed.
- 首先,執行try和except關鍵字之間的語句
- If no exception occurs, the except clause is skipped and execution of the try statement is finished.
- 如果沒有異常發生,except語句被忽略,try語句執行完畢。
- If an exception occurs during execution of the try clause, the rest of the clause is skipped. Then if its type matches the exception named after the except keyword, the except clause is executed, and then execution continues after the try statement.
- 如果在try語句中發生異常,則打斷try后面的語句。如果異常的類型匹配except后面的類型,則except語句塊被執行。然后再執行try-except塊后面的語句。
- If an exception occurs which does not match the exception named in the except clause, it is passed on to outer try statements; if no handler is found, it is an unhandled exception and execution stops with a message as shown above.
- 如果發生的異常類型和except后面的異常類型不匹配,則異常會被傳遞到try語句的外面,如果外層也沒有出來該,則會終止程序的運行,並返回錯誤消息。
A try statement may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed. Handlers only handle exceptions that occur in the corresponding try clause, not in other handlers of the same try statement. An except clause may name multiple exceptions as a parenthesized tuple, for example:
一個try語句有可能不止一個except語句塊,可以為不同的異常指定處理程序。但至多只有一個處理程序會執行。處理程序僅僅只處理try語句中相應的異常類型,而不會處理同一個try語句的其他異常。一個except語句塊可以包含多個異常類型,例如:
... except (RuntimeError, TypeError, NameError): ... pass
The last except clause may omit the exception name(s), to serve as a wildcard. Use this with extreme caution, since it is easy to mask a real programming error in this way! It can also be used to print an error message and then re-raise the exception (allowing a caller to handle the exception as well):
try-except的最后一個except語句塊可以省略異常的名字,來作為一個通配符。使用這種異常塊要非常的小心,因為這種方式很容易掩蓋程序真實的錯誤!它也能夠先打印錯誤消息,並引發異常(允許調用函數來處理異常)。
import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise
The try ... except statement has an optional else clause, which, when present, must follow all except clauses. It is useful for code that must be executed if the try clause does not raise an exception. For example:
try...except語句有個可選擇的else語句塊,它必須跟隨在所有的except語句塊之后。它的作用是,如果try語句塊沒有引發異常,則else代碼塊必須執行。
for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print('cannot open', arg) else: print(arg, 'has', len(f.readlines()), 'lines') f.close()
The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try ... except statement.
使用else語句塊要比在try語句中增加額外的語句更好,因為它避免了意外的異常捕捉,這這個異常可能並不是try...excepty語句中要捕捉的。
When an exception occurs, it may have an associated value, also known as the exception’s argument. The presence and type of the argument depend on the exception type.
當發生異常時,它可能有個關聯值,也叫做異常的參數。這個參數的類型以及是否有這個參數取決於異常類型。
The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in instance.args. For convenience, the exception instance defines __str__() so the arguments can be printed directly without having to reference .args. One may also instantiate an exception first before raising it and add any attributes to it as desired.
except語句塊可以在異常名字的后面指定一個變量。這個變量綁定了該異常的實例,並且異常的參數存儲在instance.args。為了方便,異常實例定義了一個__str()__函數,因此參數可以直接打印而不必引用.args。可能有人想在拋出異常前先初始化異常實例,給該異常增加一些所期望的屬性。
>>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print(type(inst)) # the exception instance ... print(inst.args) # arguments stored in .args ... print(inst) # __str__ allows args to be printed directly, ... # but may be overridden in exception subclasses ... x, y = inst.args # unpack args ... print('x =', x) ... print('y =', y) ... <class 'Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs
If an exception has arguments, they are printed as the last part (‘detail’) of the message for unhandled exceptions.
如果一個異常帶有參數,這個異常如果沒有被處理,那么它的這些參數會在錯誤消息的最后面打印出來。
Exception handlers don’t just handle exceptions if they occur immediately in the try clause, but also if they occur inside functions that are called (even indirectly) in the try clause. For example:
異常處理程序不僅僅只處理在try語句塊中出現的異常,而且還可以處理在try語句中所包括的函數內部的異常。例如:
>>> def this_fails(): ... x = 1/0 ... >>> try: ... this_fails() ... except ZeroDivisionError as err: ... print('Handling run-time error:', err) ... Handling run-time error: int division or modulo by zero
8.4. Raising Exceptions
The raise statement allows the programmer to force a specified exception to occur. For example:
raise語句運行程序員迫使指定的異常發生。例如:
>>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in ? NameError: HiThere
The sole argument to raise indicates the exception to be raised. This must be either an exception instance or an exception class (a class that derives from Exception).
raise唯一的參數表明了什么異常被拋出。這個參數必須是異常實例或者是Exception類及子類。
If you need to determine whether an exception was raised but don’t intend to handle it, a simpler form of the raise statement allows you to re-raise the exception:
如果你需要知道哪個異常被拋出,但你又不想處理它,一個簡單的raise語句允許你重新拋出該異常。
>>> try: ... raise NameError('HiThere') ... except NameError: ... print('An exception flew by!') ... raise ... An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in ? NameError: HiThere
8.5. User-defined Exceptions
Programs may name their own exceptions by creating a new exception class (see Classes for more about Python classes). Exceptions should typically be derived from the Exception class, either directly or indirectly. For example:
程序者可能希望自己能創建一個新的異常類。那么這個異常類必須直接或間接的繼承Exception類。例如:
>>> class MyError(Exception): ... def __init__(self, value): ... self.value = value ... def __str__(self): ... return repr(self.value) ... >>> try: ... raise MyError(2*2) ... except MyError as e: ... print('My exception occurred, value:', e.value) ... My exception occurred, value: 4 >>> raise MyError('oops!') Traceback (most recent call last): File "<stdin>", line 1, in ? __main__.MyError: 'oops!'
In this example, the default __init__() of Exception has been overridden. The new behavior simply creates the value attribute. This replaces the default behavior of creating the args attribute.
在這個例子中,Exception類默認的__init__()函數被覆蓋了。新的__init__()函數僅僅創建一個value屬性。這個替換了args屬性的默認行為。
Exception classes can be defined which do anything any other class can do, but are usually kept simple, often only offering a number of attributes that allow information about the error to be extracted by handlers for the exception. When creating a module that can raise several distinct errors, a common practice is to create a base class for exceptions defined by that module, and subclass that to create specific exception classes for different error conditions:
一個異常類可以像其他類一樣做任何事情,但是通常都是保持簡單的形式,通常是僅僅提供一些屬性來關聯錯誤信息,並當處理程序引發異常時調用。當創建一個模塊可以引發幾種不同的錯誤時,一個普遍的做法是創建一個基類,然后子類處理不同的異常錯誤類型。
class Error(Exception): """Base class for exceptions in this module.""" pass class InputError(Error): """Exception raised for errors in the input. Attributes: expression -- input expression in which the error occurred message -- explanation of the error """ def __init__(self, expression, message): self.expression = expression self.message = message class TransitionError(Error): """Raised when an operation attempts a state transition that's not allowed. Attributes: previous -- state at beginning of transition next -- attempted new state message -- explanation of why the specific transition is not allowed """ def __init__(self, previous, next, message): self.previous = previous self.next = next self.message = message
Most exceptions are defined with names that end in “Error,” similar to the naming of the standard exceptions.
大多數自定義的異常類的名字都是以"Error"結尾,這類似於標准異常類的風格。
Many standard modules define their own exceptions to report errors that may occur in functions they define. More information on classes is presented in chapter Classes.
許多標准的模塊都定義了他們自己的異常來報告錯誤。關於類的描述,請查閱Classes章節。
8.6. Defining Clean-up Actions
The try statement has another optional clause which is intended to define clean-up actions that must be executed under all circumstances. For example:
try語句還有另一個可選的子塊,這個子塊主要是用來做一些清理工作。
>>> try: ... raise KeyboardInterrupt ... finally: ... print('Goodbye, world!') ... Goodbye, world! KeyboardInterrupt
A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. When an exception has occurred in the try clause and has not been handled by an except clause (or it has occurred in a except or else clause), it is re-raised after the finally clause has been executed. The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement. A more complicated example:
在例子try語句之前,finally子塊總是必須執行,不管是否發生異常。當一個異常在try語句塊中引發而沒有except子塊處理,它會在finally子塊執行完后引發異常。不管在try語句塊中時由於break,continue或者return語句中斷了,finally語句還是要執行。例如:
>>> def divide(x, y): ... try: ... result = x / y ... except ZeroDivisionError: ... print("division by zero!") ... else: ... print("result is", result) ... finally: ... print("executing finally clause") ... >>> divide(2, 1) result is 2.0 executing finally clause >>> divide(2, 0) division by zero! executing finally clause >>> divide("2", "1") executing finally clause Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 3, in divide TypeError: unsupported operand type(s) for /: 'str' and 'str'
As you can see, the finally clause is executed in any event. The TypeError raised by dividing two strings is not handled by the except clause and therefore re-raised after the finally clause has been executed.
正如你所看見的,finally子塊總是會執行。兩個字符串相除引發的TypeError異常沒有被except子塊處理,因此,在finally子塊執行完后重新拋出。
In real world applications, the finally clause is useful for releasing external resources (such as files or network connections), regardless of whether the use of the resource was successful.
在真實的程序世界中,fianlly子塊通常是用來釋放一下額外的資源,不管資源是否被使用成功。
8.7. Predefined Clean-up Actions
Some objects define standard clean-up actions to be undertaken when the object is no longer needed, regardless of whether or not the operation using the object succeeded or failed. Look at the following example, which tries to open a file and print its contents to the screen.
有些對象定義了標准的清理操作。當對象不再需要使用的時候,就會自動清理資源,不管對象調用的操作是否成功。看下面的例子,嘗試打開一個文件,並將內容輸出到屏幕上。
for line in open("myfile.txt"): print(line, end="")
The problem with this code is that it leaves the file open for an indeterminate amount of time after this part of the code has finished executing. This is not an issue in simple scripts, but can be a problem for larger applications. The with statement allows objects like files to be used in a way that ensures they are always cleaned up promptly and correctly.
這段代碼的問題是:當這部分的代碼執行完畢后,文件一直處於打開的狀態。這在一個簡單的腳本中不會有什么問題,但是在一個大的程序中可能會出現問題。with語句允許類似於文件對象的對象在使用后,確保他們總是合適的、正確的被清理掉。
with open("myfile.txt") as f: for line in f: print(line, end="")
After the statement is executed, the file f is always closed, even if a problem was encountered while processing the lines. Objects which, like files, provide predefined clean-up actions will indicate this in their documentation.
當語句執行后,f對象總是會關閉,既使在處理文件的過程中出現了問題。類似於文件的對象他們提供了提前清理資源的操作,這些內容可以參閱這些對象相關的文檔。
