Câu trả lời:
Làm cách nào để tự ném / nâng một ngoại lệ trong Python?
Sử dụng hàm tạo ngoại lệ cụ thể nhất phù hợp với ngữ nghĩa của bạn .
Hãy cụ thể trong tin nhắn của bạn, ví dụ:
raise ValueError('A very specific bad thing happened.')
Tránh nâng cao chung chung Exception
. Để nắm bắt nó, bạn sẽ phải nắm bắt tất cả các trường hợp ngoại lệ cụ thể khác mà phân lớp nó.
raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.
Ví dụ:
def demo_bad_catch():
try:
raise ValueError('Represents a hidden bug, do not catch this')
raise Exception('This is the exception you expect to handle')
except Exception as error:
print('Caught this error: ' + repr(error))
>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)
Và các sản phẩm khai thác cụ thể hơn sẽ không bắt được ngoại lệ chung:
def demo_no_catch():
try:
raise Exception('general exceptions not caught by specific handling')
except ValueError as e:
print('we will not catch exception: Exception')
>>> demo_no_catch()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling
raise
tuyên bốThay vào đó, hãy sử dụng hàm tạo Exception cụ thể nhất phù hợp với ngữ nghĩa của bạn .
raise ValueError('A very specific bad thing happened')
cũng có thể cho phép một số lượng các đối số tùy ý được truyền cho hàm tạo:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
Các đối số này được truy cập bởi args
thuộc tính trên Exception
đối tượng. Ví dụ:
try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)
in
('message', 'foo', 'bar', 'baz')
Trong Python 2.5, một message
thuộc tính thực tế đã được thêm vào để BaseException
khuyến khích người dùng phân lớp Ngoại lệ và ngừng sử dụng args
, nhưng việc giới thiệu message
và khấu hao ban đầu của các đối số đã bị rút lại .
except
mệnh đềVí dụ, khi bên trong một mệnh đề ngoại trừ, bạn có thể muốn ghi lại rằng một loại lỗi cụ thể đã xảy ra và sau đó nâng lại. Cách tốt nhất để làm điều này trong khi lưu giữ dấu vết ngăn xếp là sử dụng câu lệnh tăng trần. Ví dụ:
logger = logging.getLogger(__name__)
try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!
Bạn có thể bảo vệ stacktrace (và giá trị lỗi) sys.exc_info()
, nhưng đây là cách dễ bị lỗi hơn và có vấn đề tương thích giữa Python 2 và 3 , thích sử dụng mức trần raise
để nâng cao lại.
Để giải thích - sys.exc_info()
trả về loại, giá trị và truy nguyên.
type, value, traceback = sys.exc_info()
Đây là cú pháp trong Python 2 - lưu ý rằng điều này không tương thích với Python 3:
raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
Nếu bạn muốn, bạn có thể sửa đổi những gì xảy ra với mức tăng mới của mình - ví dụ: thiết lập mới args
cho ví dụ:
def error():
raise ValueError('oops!')
def catch_error_modify_message():
try:
error()
except ValueError:
error_type, error_instance, traceback = sys.exc_info()
error_instance.args = (error_instance.args[0] + ' <modification>',)
raise error_type, error_instance, traceback
Và chúng tôi đã bảo toàn toàn bộ truy nguyên trong khi sửa đổi các đối số. Lưu ý rằng đây không phải là cách thực hành tốt nhất và đó là cú pháp không hợp lệ trong Python 3 (khiến việc giữ khả năng tương thích trở nên khó khăn hơn nhiều).
>>> catch_error_modify_message()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in catch_error_modify_message
File "<stdin>", line 2, in error
ValueError: oops! <modification>
Trong Python 3 :
raise error.with_traceback(sys.exc_info()[2])
Một lần nữa: tránh thao tác thủ công theo dõi. Nó ít hiệu quả hơn và dễ bị lỗi hơn. Và nếu bạn đang sử dụng luồng và sys.exc_info
thậm chí bạn có thể nhận được dấu vết sai (đặc biệt là nếu bạn đang sử dụng xử lý ngoại lệ cho luồng điều khiển - điều mà cá nhân tôi có xu hướng tránh.)
Trong Python 3, bạn có thể xâu chuỗi các trường hợp ngoại lệ, bảo tồn các dấu vết:
raise RuntimeError('specific message') from error
Hãy lưu ý:
Chúng có thể dễ dàng ẩn và thậm chí vào mã sản xuất. Bạn muốn đưa ra một ngoại lệ, và thực hiện chúng sẽ đưa ra một ngoại lệ, nhưng không phải là một dự định!
Hợp lệ trong Python 2, nhưng không phải trong Python 3 là như sau:
raise ValueError, 'message' # Don't do this, it's deprecated!
Chỉ hợp lệ trong các phiên bản cũ hơn của Python (2.4 trở xuống), bạn vẫn có thể thấy mọi người nâng chuỗi:
raise 'message' # really really wrong. don't do this.
Trong tất cả các phiên bản hiện đại, điều này thực sự sẽ tăng một TypeError
, bởi vì bạn không nâng một BaseException
loại. Nếu bạn không kiểm tra ngoại lệ phù hợp và không có người đánh giá nhận thức được vấn đề, nó có thể được đưa vào sản xuất.
Tôi đưa ra Ngoại lệ để cảnh báo người tiêu dùng API của mình nếu họ sử dụng không đúng cách:
def api_func(foo):
'''foo should be either 'baz' or 'bar'. returns something very useful.'''
if foo not in _ALLOWED_ARGS:
raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))
"Tôi muốn tạo ra một lỗi về mục đích, để nó đi vào ngoại lệ"
Bạn có thể tạo các loại lỗi của riêng mình, nếu bạn muốn chỉ ra một cái gì đó cụ thể là sai với ứng dụng của bạn, chỉ cần phân lớp điểm thích hợp trong hệ thống phân cấp ngoại lệ:
class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''
và cách sử dụng:
if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]
dường như làm những gì tôi muốn, và tôi chưa bao giờ gặp vấn đề với nó. Nhưng nó cảm thấy hacky, và không phải là một thực tế được chấp nhận. Có cách nào tốt hơn?
Exception
lớp cha mẹ của bạn - bạn có thể phân lớp một cái gì đó cụ thể hơn và nên làm như vậy nếu nó hợp lý.
AppError
ngoại lệ không xác định . Có thể tốt hơn để sử dụng một lỗi tích hợp nhưAttributeError
ĐỪNG LÀM ĐIỀU NÀY . Nâng trần
Exception
hoàn toàn không phải là điều nên làm; thay vào đó hãy xem câu trả lời xuất sắc của Aaron Hall .
Không thể nhận được nhiều pythonic hơn thế này:
raise Exception("I know python!")
Xem tài liệu tuyên bố nâng cao cho python nếu bạn muốn biết thêm thông tin.
Trong Python3 có 4 cú pháp khác nhau để loại trừ các ngoại lệ:
1. raise exception
2. raise exception (args)
3. raise
4. raise exception (args) from original_exception
1. tăng ngoại lệ so với 2. tăng ngoại lệ (args)
Nếu bạn sử dụng raise exception (args)
để đưa ra một ngoại lệ thì nó args
sẽ được in khi bạn in đối tượng ngoại lệ - như trong ví dụ dưới đây.
#raise exception (args)
try:
raise ValueError("I have raised an Exception")
except ValueError as exp:
print ("Error", exp) # Output -> Error I have raised an Exception
#raise execption
try:
raise ValueError
except ValueError as exp:
print ("Error", exp) # Output -> Error
3.raise
raise
tuyên bố mà không có bất kỳ đối số lại làm tăng ngoại lệ cuối cùng. Điều này rất hữu ích nếu bạn cần thực hiện một số hành động sau khi bắt ngoại lệ và sau đó muốn nâng lại nó. Nhưng nếu không có ngoại lệ trước đó, raise
tuyên bố sẽ tăng TypeError
Ngoại lệ.
def somefunction():
print("some cleaning")
a=10
b=0
result=None
try:
result=a/b
print(result)
except Exception: #Output ->
somefunction() #some cleaning
raise #Traceback (most recent call last):
#File "python", line 8, in <module>
#ZeroDivisionError: division by zero
4. đưa ra ngoại lệ (args) từ original_exception
Tuyên bố này được sử dụng để tạo chuỗi ngoại lệ trong đó một ngoại lệ được nêu ra để đáp ứng với ngoại lệ khác có thể chứa các chi tiết của ngoại lệ ban đầu - như trong ví dụ dưới đây.
class MyCustomException(Exception):
pass
a=10
b=0
reuslt=None
try:
try:
result=a/b
except ZeroDivisionError as exp:
print("ZeroDivisionError -- ",exp)
raise MyCustomException("Zero Division ") from exp
except MyCustomException as exp:
print("MyException",exp)
print(exp.__cause__)
Đầu ra:
ZeroDivisionError -- division by zero
MyException Zero Division
division by zero
exception(args)
hơnexception (args)
raise exception(args) from None
cho rằng ngoại lệ hiện đang hoạt động đã được xử lý và không còn được quan tâm. Nếu không, nếu bạn tăng một ngoại lệ bên trong một except
khối và nó không được xử lý, tracebacks cho cả trường hợp ngoại lệ sẽ được hiển thị phân cách bằng thông báo “Trong xử lý các ngoại lệ nêu trên, ngoại lệ khác xảy ra”
Đối với trường hợp phổ biến khi bạn cần đưa ra một ngoại lệ để đáp ứng với một số điều kiện không mong muốn và bạn không bao giờ có ý định bắt, nhưng đơn giản là không thể nhanh chóng cho phép bạn gỡ lỗi từ đó nếu điều đó xảy ra - điều hợp lý nhất dường như là AssertionError
:
if 0 < distance <= RADIUS:
#Do something.
elif RADIUS < distance:
#Do something.
else:
raise AssertionError("Unexpected value of 'distance'!", distance)
ValueError
hơn AssertionError
vì không có vấn đề gì với một xác nhận (vì không có gì được thực hiện ở đây) - vấn đề là ở một giá trị. Nếu bạn thực sự muốn một AssertionError
trong trường hợp này, viết assert distance > 0, 'Distance must be positive'
. Nhưng bạn không nên kiểm tra lỗi theo cách đó vì các xác nhận có thể bị tắt ( python -O
).
-O
.
Đọc các câu trả lời hiện có trước, đây chỉ là phần phụ lục.
Lưu ý rằng bạn có thể đưa ra các ngoại lệ có hoặc không có đối số.
Thí dụ:
raise SystemExit
thoát khỏi chương trình nhưng bạn có thể muốn biết chuyện gì đã xảy ra. Vì vậy, bạn có thể sử dụng chương trình này.
raise SystemExit("program exited")
điều này sẽ in "chương trình đã thoát" sang thiết bị lỗi chuẩn trước khi đóng chương trình.
raise SystemExit()
là sự lựa chọn tốt hơn? Tại sao cái đầu tiên thậm chí hoạt động?
Một cách khác để ném một ngoại lệ là assert
. Bạn có thể sử dụng khẳng định để xác minh một điều kiện đang được thực hiện nếu không thì nó sẽ tăng AssertionError
. Để biết thêm chi tiết có một cái nhìn ở đây .
def avg(marks):
assert len(marks) != 0,"List is empty."
return sum(marks)/len(marks)
mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))
mark1 = []
print("Average of mark1:",avg(mark1))
Chỉ cần lưu ý: có những lúc bạn KHÔNG muốn xử lý các trường hợp ngoại lệ chung chung. Nếu bạn đang xử lý một loạt các tệp và ghi lại lỗi của mình, bạn có thể muốn bắt bất kỳ lỗi nào xảy ra đối với một tệp, ghi nhật ký và tiếp tục xử lý các tệp còn lại. Trong trường hợp đó, một
try:
foo()
except Exception as e:
print(str(e)) # Print out handled error
chặn một cách tốt để làm điều đó Bạn vẫn sẽ muốn raise
ngoại lệ cụ thể để bạn biết ý nghĩa của chúng, mặc dù.
Bạn nên tìm hiểu tuyên bố nâng cao của python cho điều đó. Nó nên được giữ trong khối thử. Thí dụ -
try:
raise TypeError #remove TypeError by any other error if you want
except TypeError:
print('TypeError raised')
raise
là những gì tôi cần để có thể thực hiện gỡ lỗi lỗi tùy chỉnh ở nhiều cấp độ thực thi mã mà không phá vỡ dấu vết ngăn xếp.