python: Làm thế nào để tôi biết loại ngoại lệ xảy ra?


230

Tôi có một chức năng được gọi bởi chương trình chính:

try:
    someFunction()
except:
    print "exception happened!"

nhưng ở giữa thực thi chức năng, nó làm tăng ngoại lệ, vì vậy nó nhảy đến exceptmột phần.

Làm thế nào tôi có thể thấy chính xác những gì đã xảy ra trong someFunction()đó gây ra ngoại lệ xảy ra?


9
Không bao giờ sử dụng trần except:(không có trần raise), ngoại trừ có thể một lần cho mỗi chương trình, và tốt nhất là không sau đó.
Mike Graham

Nếu bạn sử dụng nhiều exceptmệnh đề bạn không cần kiểm tra loại ngoại lệ, đó là điều thường được thực hiện để hành động tương ứng với một loại ngoại lệ cụ thể.
Rik Poggi

3
Nếu bạn quan tâm đến loại ngoại lệ, thì đó là vì bạn đã xem xét loại ngoại lệ nào có thể xảy ra một cách hợp lý.
Karl Knechtel

3
Bên trong exceptkhối ngoại lệ có sẵn thông qua sys.exc_info()chức năng - Hàm này trả về một bộ ba giá trị cung cấp thông tin về ngoại lệ hiện đang được xử lý.
Piotr Dobrogost

Câu trả lời:


384

Các câu trả lời khác đều chỉ ra rằng bạn không nên bắt ngoại lệ chung chung, nhưng dường như không ai muốn nói cho bạn biết tại sao, điều cần thiết để hiểu khi bạn có thể phá vỡ "quy tắc". Đây là một lời giải thích. Về cơ bản, đó là để bạn không che giấu:

  • thực tế là đã xảy ra lỗi
  • các chi tiết cụ thể của lỗi đã xảy ra ( lỗi ẩn antipotype )

Vì vậy, miễn là bạn cẩn thận không làm bất cứ điều gì trong số đó, thì không sao để bắt ngoại lệ chung. Chẳng hạn, bạn có thể cung cấp thông tin về ngoại lệ cho người dùng theo cách khác, như:

  • Trình bày các ngoại lệ dưới dạng hộp thoại trong GUI
  • Chuyển ngoại lệ từ luồng xử lý hoặc tiến trình sang luồng điều khiển hoặc xử lý trong ứng dụng đa luồng hoặc đa xử lý

Vậy làm thế nào để bắt ngoại lệ chung? Có một số cách. Nếu bạn chỉ muốn đối tượng ngoại lệ, hãy làm như thế này:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

Hãy chắc chắn message được đưa đến sự chú ý của người dùng một cách khó bỏ lỡ! In nó, như được hiển thị ở trên, có thể không đủ nếu tin nhắn bị chôn vùi trong nhiều tin nhắn khác. Không thu hút được sự chú ý của người dùng là tương đương với việc nuốt tất cả các ngoại lệ, và nếu có một ấn tượng bạn nên bỏ qua sau khi đọc câu trả lời trên trang này, thì đó không phải là một điều tốt . Kết thúc khối ngoại trừ bằng một raisetuyên bố sẽ khắc phục vấn đề bằng cách minh bạch lại ngoại lệ đã bị bắt.

Sự khác biệt giữa ở trên và chỉ sử dụng except:mà không có bất kỳ đối số nào là gấp đôi:

  • Một trần except:không cung cấp cho bạn đối tượng ngoại lệ để kiểm tra
  • Các trường hợp ngoại lệ SystemExit, KeyboardInterruptGeneratorExitkhông được đánh bắt bởi các mã trên, mà nói chung những gì bạn muốn là. Xem hệ thống phân cấp ngoại lệ .

Nếu bạn cũng muốn cùng một stacktrace bạn nhận được nếu bạn không bắt được ngoại lệ, bạn có thể nhận được như thế này (vẫn nằm trong mệnh đề ngoại trừ):

import traceback
print traceback.format_exc()

Nếu bạn sử dụng loggingmô-đun, bạn có thể in ngoại lệ vào nhật ký (cùng với một thông báo) như thế này:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

Nếu bạn muốn đào sâu hơn và kiểm tra ngăn xếp, hãy xem các biến, v.v., sử dụng post_mortemchức năng của pdbmô-đun bên trong khối ngoại trừ:

import pdb
pdb.post_mortem()

Tôi đã tìm thấy phương pháp cuối cùng này là vô giá khi săn lùng các lỗi.


1
tracBack.print_exc () sẽ làm điều tương tự như "" .join-thing phức tạp hơn của bạn, tôi nghĩ vậy.
Gurgeh

1
@Gurgeh Có, nhưng tôi không biết liệu anh ấy có muốn in nó hay lưu nó vào một tập tin hoặc đăng nhập nó hoặc làm một cái gì đó khác với nó.
Lauritz V. Thaulow

Tôi đã không đánh giá thấp, nhưng tôi nói rằng vì bạn nên đặt một lượng lớn chất béo vào lúc đầu nói rằng bạn không cần bất kỳ thứ gì trong số này, nhưng đây là cách nó có thể được thực hiện . Và có thể bởi vì bạn đề nghị bắt Ngoại lệ chung.
Rik Poggi

10
@Rik Tôi nghĩ bạn rất cần tất cả những thứ này. Ví dụ: nếu bạn có một chương trình có GUI và phụ trợ và bạn muốn trình bày tất cả các ngoại lệ từ phụ trợ dưới dạng tin nhắn GUI thay vì chương trình của bạn thoát khỏi dấu vết ngăn xếp. Trong trường hợp như vậy, bạn nên bắt Ngoại lệ chung , tạo văn bản theo dõi cho hộp thoại, ghi nhật ký ngoại lệ và nếu ở chế độ gỡ lỗi, hãy nhập nội dung sau.
Lauritz V. Thaulow

18
@RikPoggi: Suy nghĩ ngây thơ. Có nhiều trường hợp hợp lý khi bạn cần bắt ngoại lệ từ mã của người khác và bạn không biết ngoại lệ nào sẽ được nêu ra.
stackoverflowuser2010

63

Lấy tên của lớp mà đối tượng ngoại lệ thuộc về:

e.__class__.__name__

và sử dụng hàm print_exc () cũng sẽ in dấu vết ngăn xếp là thông tin cần thiết cho bất kỳ thông báo lỗi nào.

Như thế này:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

Bạn sẽ nhận được đầu ra như thế này:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

Và sau khi in và phân tích, mã có thể quyết định không xử lý ngoại lệ và chỉ thực thi raise:

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

Đầu ra:

special case of CustomException not interfering

Và phiên dịch in ngoại lệ:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

Sau khi raisengoại lệ ban đầu tiếp tục truyền thêm lên ngăn xếp cuộc gọi. ( Cẩn thận với cạm bẫy có thể xảy ra ) Nếu bạn đưa ra ngoại lệ mới, nó sẽ theo dõi ngăn xếp ngăn xếp mới (ngắn hơn).

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

Đầu ra:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

Lưu ý cách truy nguyên không bao gồm calculate()chức năng từ dòng 9vốn là nguồn gốc của ngoại lệ ban đầu e.


Nếu bạn muốn lưu trữ traceback như là một chuỗi, bạn có thể sử dụng traceback.format_exc()cũng
Stevoisiak

1
e.__class__.__name__điều này có giống như type(e).__name__, như được đề xuất bởi câu trả lời ở trên không?
information_interchange

1
@in information_interchange có. Các câu hỏi và nội dung câu trả lời được chấp nhận thay đổi hoàn toàn theo thời gian. Thật xấu hổ khi những người tham gia khác không được thông báo bởi bộ máy SO :(
Alex

14

Bạn thường không nên nắm bắt tất cả các ngoại lệ có thể với try: ... exceptvì điều này là quá rộng. Chỉ cần bắt những người dự kiến ​​sẽ xảy ra vì lý do gì. Nếu bạn thực sự phải, ví dụ nếu bạn muốn tìm hiểu thêm về một số vấn đề trong khi gỡ lỗi, bạn nên làm

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.

17
Việc sử dụng từ "không bao giờ" ở đây chưa bao giờ sai. Tôi sử dụng try: ... except Exception:xung quanh rất nhiều thứ, ví dụ như việc sử dụng các thư viện phụ thuộc mạng hoặc một nhân viên mát xa dữ liệu có thể nhận được những thứ kỳ lạ được gửi cho cô ấy. Tự nhiên tôi cũng đăng nhập đúng. Điều này là rất quan trọng để cho phép chương trình tiếp tục hoạt động trong trường hợp có một lỗi duy nhất trong dữ liệu đầu vào.
thnee

3
Bạn đã bao giờ cố gắng nắm bắt tất cả các ngoại lệ có thể được nêu ra khi gửi email bằng cách sử dụng smtplibchưa?
linusg

1
Có thể có một số trường hợp đặc biệt khi bắt tất cả các ngoại lệ là cần thiết, nhưng ở cấp độ chung, bạn chỉ nên nắm bắt những gì bạn mong đợi để không vô tình che giấu các lỗi mà bạn không lường trước được. Tất nhiên, đăng nhập tốt là một ý tưởng tốt.
hochl

1
Nó là hoàn toàn hợp lý để nắm bắt tất cả các ngoại lệ. Nếu bạn đang gọi thư viện của bên thứ ba, bạn có thể không biết ngoại lệ nào sẽ được nêu ra trong thư viện đó. Trong trường hợp như vậy, cách duy nhất là bắt tất cả các ngoại lệ, ví dụ để ghi lại chúng trong một tệp.
stackoverflowuser2010

Ok ok bạn nói đúng, tôi sẽ viết lại câu trả lời của mình để làm rõ rằng có những trường hợp sử dụng hợp lệ cho tất cả.
hochl

10

Trừ khi somefunctionlà một chức năng kế thừa được mã hóa rất tệ, bạn không nên cần những gì bạn yêu cầu.

Sử dụng nhiều exceptmệnh đề để xử lý theo những cách khác nhau ngoại lệ khác nhau:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

Điểm chính là bạn không nên bắt ngoại lệ chung chung, mà chỉ những cái bạn cần. Tôi chắc chắn rằng bạn không muốn che giấu các lỗi hoặc lỗi không mong muốn.


8
Nếu bạn đang sử dụng thư viện của bên thứ ba, bạn có thể không biết ngoại lệ nào sẽ được nêu ra bên trong thư viện. Làm thế nào bạn có thể bắt tất cả chúng riêng lẻ?
stackoverflowuser2010

8

Hầu hết các câu trả lời đều chỉ đến except (…) as (…):cú pháp (đúng như vậy) nhưng đồng thời không ai muốn nói về một con voi trong phòng, nơi con voi đang sys.exc_info()hoạt động. Từ tài liệu của mô-đun sys (nhấn mạnh của tôi):

Hàm này trả về một bộ ba giá trị cung cấp thông tin về ngoại lệ hiện đang được xử lý.
(Vắc)
Nếu không có ngoại lệ nào được xử lý ở bất kỳ đâu trên ngăn xếp, một bộ dữ liệu chứa ba giá trị Không được trả về. Mặt khác, các giá trị được trả về là (loại, giá trị, truy nguyên). Ý nghĩa của chúng là: type lấy loại ngoại lệ được xử lý (một lớp con của BaseException); giá trị lấy trường hợp ngoại lệ (một thể hiện của loại ngoại lệ); truy nguyên được một đối tượng theo dõi (xem Hướng dẫn tham khảo) gói gọn ngăn xếp cuộc gọi tại điểm xảy ra ngoại lệ ban đầu.

Tôi nghĩ rằng sys.exc_info()câu trả lời có thể được coi là câu trả lời trực tiếp nhất cho câu hỏi ban đầu về Làm thế nào để tôi biết loại ngoại lệ nào xảy ra?


1
Đó là câu trả lời chính xác cho tôi vì nó giải quyết được vấn đề ngoại lệ nào đang xảy ra, vậy tôi nên đặt cái gì thay vì để trần except. Chỉ vì mục đích hoàn chỉnh, exctype, value = sys.exc_info()[:2]sẽ cho bạn biết loại ngoại lệ có thể được sử dụng trên except.
Ondrej Burkert

5

thử: someFunction () ngoại trừ Ngoại lệ, exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))

-1 như sử dụng exc.__class__.__name__đã được đề xuất trong câu trả lời của Alex - stackoverflow.com/a/9824060/95735
Piotr Dobrogost

3

Các câu trả lời này rất tốt để gỡ lỗi, nhưng để kiểm tra ngoại lệ theo chương trình, isinstance(e, SomeException)có thể có ích, vì nó cũng kiểm tra các lớp con SomeException, vì vậy bạn có thể tạo chức năng áp dụng cho phân cấp ngoại lệ.


1

Đây là cách tôi xử lý ngoại lệ của mình. Ý tưởng là cố gắng giải quyết vấn đề nếu điều đó dễ dàng, và sau đó thêm một giải pháp mong muốn hơn nếu có thể. Không giải quyết vấn đề trong mã tạo ra ngoại lệ hoặc mã đó mất dấu vết của thuật toán gốc, mã này sẽ được ghi vào điểm. Tuy nhiên, vượt qua những dữ liệu cần thiết để giải quyết vấn đề và trả lại lambda chỉ trong trường hợp bạn không thể giải quyết vấn đề bên ngoài mã tạo ra nó.

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

Hiện tại, vì tôi không muốn nghĩ theo cách tiếp cận mục đích của ứng dụng của mình, tôi đã không thêm bất kỳ giải pháp phức tạp nào. Nhưng trong tương lai, khi tôi biết nhiều hơn về các giải pháp khả thi (vì ứng dụng được thiết kế nhiều hơn), tôi có thể thêm vào một từ điển các giải pháp được lập chỉ mục bởi during.

Trong ví dụ hiển thị, một giải pháp có thể là tìm kiếm dữ liệu ứng dụng được lưu trữ ở một nơi khác, giả sử nếu tệp 'app.p' bị xóa do nhầm lẫn.

Hiện tại, vì viết trình xử lý ngoại lệ không phải là một ý tưởng thông minh (chúng tôi chưa biết cách tốt nhất để giải quyết nó, vì thiết kế ứng dụng sẽ phát triển), chúng tôi chỉ cần trả về cách khắc phục dễ dàng như hành động như chúng tôi đang chạy ứng dụng lần đầu tiên (trong trường hợp này).


0

Để thêm vào câu trả lời của Lauritz, tôi đã tạo một trình trang trí / trình bao bọc để xử lý ngoại lệ và nhật ký trình bao bọc loại ngoại lệ xảy ra.

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

Điều này có thể được gọi trên một phương thức lớp hoặc một hàm độc lập với trình trang trí:

@general_feft_handler

Xem blog của tôi về ví dụ đầy đủ: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/


0

Bạn có thể bắt đầu như Lauritz đề xuất, với:

except Exception as ex:

và sau đó chỉ để print exthích như vậy:

try:
    #your try code here
except Exception as ex:
    print ex

Bạn có thể giải thích một chút để câu trả lời của bạn đứng một mình không?
GHC

1
chắc chắn: bạn có thể in ngoại lệ bị bắt như thế này: thử: #your thử mã ở đây ngoại trừ Ngoại lệ như ex: in ex now lỗi sẽ được in
Gura

-2

Ngoại lệ thực tế có thể được nắm bắt theo cách sau:

try:
    i = 1/0
except Exception as e:
    print e

Bạn có thể tìm hiểu thêm về các ngoại lệ từ Hướng dẫn Python .


-2

Câu hỏi của bạn là: "Làm thế nào tôi có thể thấy chính xác những gì đã xảy ra trong một số Hàm () gây ra ngoại lệ xảy ra?"

Dường như với tôi rằng bạn không hỏi về cách xử lý các trường hợp ngoại lệ không lường trước được trong mã sản xuất (như nhiều câu trả lời giả định), nhưng làm thế nào để tìm ra điều gì gây ra ngoại lệ cụ thể trong quá trình phát triển.

Cách dễ nhất là sử dụng trình gỡ lỗi có thể dừng ở nơi xảy ra ngoại lệ chưa được phát hiện, tốt nhất là không thoát, để bạn có thể kiểm tra các biến. Ví dụ, PyDev trong IDE nguồn mở Eclipse có thể làm điều đó. Để cho phép điều đó trong Eclipse, hãy mở phối cảnh Gỡ lỗi, chọn Manage Python Exception Breakpointstrong Runmenu và kiểm tra Suspend on uncaught exceptions.


-4

Chỉ cần không bắt ngoại lệ và truy nguyên mà bản in Python sẽ cho bạn biết ngoại lệ nào đã xảy ra.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.