Tôi có nên luôn chỉ định loại ngoại lệ trong câu lệnh `ngoại trừ` không?


93

Khi sử dụng PyCharm IDE, việc sử dụng except:không có loại ngoại lệ sẽ kích hoạt một lời nhắc từ IDE rằng mệnh đề ngoại lệ này là Too broad.

Tôi có nên bỏ qua lời khuyên này không? Hay là Pythonic luôn xác định loại ngoại lệ?


Nếu bạn muốn tìm hiểu thêm về điều này ngoài câu trả lời, google nuốt các ngoại lệ. Bạn có thể có tất cả các loại nên và không nên làm thú vị khác cùng với chúng. Mùi mã là một mùi khác.
Tony Hopkinson

Câu trả lời:


89

Hầu như luôn luôn tốt hơn nếu chỉ định một loại ngoại lệ rõ ràng. Nếu bạn sử dụng một except:mệnh đề rõ ràng, bạn có thể bắt gặp các ngoại lệ khác với những ngoại lệ mà bạn mong đợi - điều này có thể ẩn lỗi hoặc khiến việc gỡ lỗi các chương trình khó khăn hơn khi chúng không làm những gì bạn mong đợi.

Ví dụ: nếu bạn đang chèn một hàng vào cơ sở dữ liệu, bạn có thể muốn bắt một ngoại lệ cho biết rằng hàng đó đã tồn tại, vì vậy bạn có thể thực hiện cập nhật.

try:
    insert(connection, data)
except:
    update(connection, data)

Nếu bạn chỉ định trống except:, bạn cũng sẽ gặp lỗi ổ cắm cho biết rằng máy chủ cơ sở dữ liệu đã bị hỏng. Tốt nhất là chỉ bắt các trường hợp ngoại lệ mà bạn biết cách xử lý - chương trình thường không thành công tại điểm ngoại lệ sẽ tốt hơn là tiếp tục nhưng hành xử theo những cách bất ngờ kỳ lạ.

Một trường hợp mà bạn có thể muốn sử dụng bare except:là ở cấp cao nhất của chương trình bạn cần luôn chạy, chẳng hạn như một máy chủ mạng. Nhưng sau đó, bạn cần phải rất cẩn thận để ghi lại các ngoại lệ, nếu không sẽ không thể tìm ra điều gì đang xảy ra. Về cơ bản, chỉ nên có nhiều nhất một nơi trong một chương trình thực hiện điều này.

Một hệ quả tất yếu cho tất cả những điều này là mã của bạn không bao giờ nên làm raise Exception('some message')vì nó buộc mã khách hàng để sử dụng except:(hoặc except Exception:đó là gần như là xấu). Bạn nên xác định một ngoại lệ cụ thể cho vấn đề bạn muốn báo hiệu (có thể kế thừa từ một số lớp con ngoại lệ tích hợp sẵn như ValueErrorhoặc TypeError). Hoặc bạn nên nêu ra một ngoại lệ tích hợp cụ thể. Điều này cho phép người dùng mã của bạn cẩn thận trong việc chỉ bắt những trường hợp ngoại lệ mà họ muốn xử lý.


7
+1 Rất đúng. Thậm chí thú vị hơn với ví dụ: except:cũng bắt được (trong số nhiều thứ khác) NameErrorAttributeError, vì vậy nếu bạn viết sai chính tả một cái gì đó trong trykhối (ví dụ: hàm "insert" của bạn thực sự được gọi insert_onevì ai đó không coi trọng tính nhất quán nhiều như họ cần), nó luôn âm thầm cố gắng update().

1
Vì vậy, còn khi bạn cần đảm bảo một ngoại lệ không nằm trên trang web cuộc gọi hiện tại thì sao? Tức là - tôi đã nắm bắt được tất cả các ngoại lệ cụ thể mà tôi có thể thấy trước là bị ném, bây giờ tôi cần thêm rằng "nếu thứ này tôi chưa nghĩ đến bị ném, tôi cần ghi lại nó trước khi nó giết ngữ cảnh thực thi đang chạy" (chẳng hạn như main())?
Adam Parkin

Đó là tình huống mà tôi đang nói đến trong đoạn mở đầu 'Một trường hợp ...'. Nó chắc chắn đôi khi cần thiết, nhưng bất kỳ chương trình nhất định nào thực sự chỉ nên có một nơi thực hiện điều đó. Và bạn cần phải cẩn thận rằng nó làm rõ ràng trong nhật ký những gì đang xảy ra, nếu không bạn sẽ có một khoảng thời gian khó chịu khi cố gắng tìm ra lý do tại sao chương trình của bạn không xử lý sự kiện / yêu cầu / bất cứ điều gì một cách chính xác.
babbageclunk,

3
@delnan: Còn tệ hơn thế. except Exception:sẽ nắm bắt NameErrorAttributeErrorquá. Điều khiến điều đó trở except:nên tồi tệ là nó bắt được những thứ mà không có doanh nghiệp nào bị bắt, ví dụ: SystemExit(được nâng lên khi bạn gọi exithoặc sys.exitvà bây giờ bạn đã ngăn chặn một lối thoát dự định) và KeyboardInterrupt(một lần nữa, nếu người dùng nhấn Ctrl-C, bạn có thể không muốn để tiếp tục chạy chỉ để làm phiền họ). Chỉ cái sau mới có ý nghĩa thực sự để bắt và nó phải được bắt một cách rõ ràng. Ít nhất hãy except Exception:để hai cái đó lan truyền bình thường.
ShadowRanger

38

Bạn không nên bỏ qua những lời khuyên mà thông dịch viên cho bạn.

Từ Hướng dẫn kiểu PEP-8 cho Python:

Khi bắt các ngoại lệ, hãy đề cập đến các ngoại lệ cụ thể bất cứ khi nào có thể thay vì sử dụng mệnh đề ngoại trừ: trần.

Ví dụ, sử dụng:

 try:
     import platform_specific_module 
 except ImportError:
     platform_specific_module = None 

Mệnh đề bare but: sẽ bắt các ngoại lệ SystemExit và KeyboardInterrupt, khiến việc ngắt chương trình bằng Control-C khó hơn và có thể che giấu các sự cố khác. Nếu bạn muốn bắt tất cả các ngoại lệ báo hiệu lỗi chương trình, hãy sử dụng ngoại trừ Exception: (bare exception tương đương với ngoại trừ BaseException :).

Một nguyên tắc chung là hạn chế sử dụng mệnh đề "ngoại trừ" trần trong hai trường hợp:

Nếu trình xử lý ngoại lệ sẽ in ra hoặc ghi lại dấu vết; ít nhất người dùng sẽ biết rằng đã xảy ra lỗi. Nếu mã cần thực hiện một số công việc dọn dẹp, nhưng sau đó hãy để ngoại lệ truyền lên cùng với tăng. cố gắng ... cuối cùng có thể là một cách tốt hơn để xử lý trường hợp này.



9

Không đặc tả cho Python này.

Toàn bộ điểm ngoại lệ là giải quyết vấn đề càng gần với nơi nó gây ra càng tốt.

Vì vậy, bạn giữ mã có thể trong những trường hợp đặc biệt có thể gây ra sự cố và giải pháp "bên cạnh" nhau.

Vấn đề là bạn không thể biết tất cả các ngoại lệ có thể được ném bởi một đoạn mã. Tất cả những gì bạn có thể biết là nếu đó là một tập tin nói rằng không tìm thấy ngoại lệ, thì bạn có thể bẫy nó và nhắc người dùng lấy một tệp thực hiện hoặc hủy chức năng.

Nếu bạn thử bắt lại điều đó, thì bất kể có vấn đề gì xảy ra trong quy trình tệp của bạn (chỉ đọc, quyền, UAC, không thực sự là pdf, v.v.), mọi thứ sẽ truy cập vào tệp của bạn mà không tìm thấy bắt và người dùng của bạn đang la hét "nhưng nó ở đó, mã này là tào lao"

Bây giờ có một vài tình huống mà bạn có thể nắm bắt mọi thứ, nhưng chúng nên được lựa chọn một cách có ý thức.

Chúng bắt, hoàn tác một số hành động cục bộ (chẳng hạn như tạo hoặc khóa tài nguyên, (mở tệp trên đĩa để ghi chẳng hạn), sau đó bạn lại ném ngoại lệ, sẽ được xử lý ở cấp cao hơn)

Còn lại bạn là bạn không quan tâm tại sao nó lại sai. Ví dụ như in ấn. Bạn có thể gặp khó khăn khi nói rằng Máy in của bạn có vấn đề gì đó, vui lòng sắp xếp nó ra và không giết ứng dụng vì nó. Tương tự như vậy sẽ vô ích nếu mã của bạn thực hiện một loạt các nhiệm vụ riêng biệt bằng cách sử dụng một số loại lịch trình, bạn sẽ không muốn toàn bộ mọi thứ chết đi vì một trong các tác vụ không thành công.

Lưu ý Nếu bạn làm như trên, tôi không thể đề xuất một số loại ghi nhật ký ngoại lệ, ví dụ: thử bắt đầu nhật ký, đủ cao.


Tôi sẽ nói đó là về sự cân bằng. Bạn phải nắm bắt ngoại lệ đủ sớm để có thể phục hồi và đủ muộn để biết phải đi đâu với nó. Đó là lý do tại sao việc xử lý ngoại lệ trong Java gây ra sự tàn phá như vậy, bởi vì bạn phải gói lại ngoại lệ ở mỗi bước và bạn bị mất thông tin.
dhill

2
+1 cho "bạn không quan tâm tại sao sự cố." Tôi đang sử dụng nó ở nhiều nơi xung quanh một dòng mã, nơi tôi phân tích cú pháp ngày / giờ từ một URL. Thư viện phân tích cú pháp ngày / giờ của bên thứ ba không liệt kê tất cả các ngoại lệ mà nó có thể ném ra (Tôi đã tìm thấy OverflowError và TypeError ngoài ValueError tiêu chuẩn, nhưng có lẽ còn nhiều hơn nữa) và dù sao thì tôi thực sự không quan tâm tại sao một ngoại lệ đã được đưa ra, tôi chỉ muốn cung cấp một thông báo lỗi hợp lý cho người dùng nói rằng có gì đó không ổn với ngày / giờ.
Michael Rodby

3

Bạn cũng sẽ bắt được ví dụ Control-C với điều đó, vì vậy đừng làm điều đó trừ khi bạn "ném" nó một lần nữa. Tuy nhiên, trong trường hợp đó, bạn nên sử dụng "cuối cùng".


3

Luôn xác định loại ngoại lệ, có nhiều loại bạn không muốn bắt, giống như SyntaxError, KeyboardInterrupt, MemoryError, vv


2
sẽ sử dụng except Exception:tránh các loại trên mà chúng ta không muốn mắc phải?
HorseloverFat

except ExceptionỔn.
Ulrich Eckhardt

4
@HorseloverFat: except Exceptionbắt SyntaxErrorMemoryErrorvì nó là lớp cơ sở của chúng. KeyboardInterrupt, SystemExit(Nêu ra bởi sys.exit()) không bắt (họ là lớp con BaseException ngay lập tức)
JFS

Nghe có vẻ như nó không phải là lý tưởng - tốt hơn là chỉ định chính xác hơn.
HorseloverFat

3

Đây là những nơi tôi sử dụng ngoại trừ không có loại

  1. tạo mẫu nhanh và bẩn

Đó là cách sử dụng chính trong mã của tôi cho các trường hợp ngoại lệ không được kiểm tra

  1. hàm main () cấp cao nhất, trong đó tôi ghi lại mọi ngoại lệ không cần thiết

Tôi luôn thêm điều này, để mã sản xuất không tràn ra các dấu vết

  1. giữa các lớp ứng dụng

Tôi có hai cách để làm điều đó:

  • Cách đầu tiên để thực hiện: khi một lớp cấp cao hơn gọi một hàm cấp thấp hơn, nó sẽ bọc các lệnh gọi trong các ngoại lệ đã nhập để xử lý các ngoại lệ cấp thấp hơn "trên cùng". Nhưng tôi thêm một câu lệnh chung chung ngoại trừ, để phát hiện các ngoại lệ cấp thấp hơn chưa được xử lý trong các hàm cấp thấp hơn.

Tôi thích nó theo cách này hơn, tôi thấy dễ dàng hơn trong việc phát hiện những ngoại lệ nào lẽ ra phải được bắt một cách thích hợp: tôi "nhìn thấy" vấn đề tốt hơn khi một ngoại lệ cấp thấp hơn được ghi lại bởi cấp cao hơn

  • Cách thứ hai để làm điều đó: mỗi hàm cấp cao nhất của các lớp cấp thấp hơn có mã của chúng được bao bọc trong một ngoại trừ chung, để nó bắt tất cả các ngoại lệ không được xử lý trên lớp cụ thể đó.

Một số đồng nghiệp thích cách này, vì nó giữ các ngoại lệ cấp thấp hơn trong các chức năng cấp thấp hơn, nơi họ "thuộc về".


-14

Thử cái này:

try:
    #code
except ValueError:
    pass

Tôi đã nhận được câu trả lời từ liên kết này, nếu có ai khác gặp phải vấn đề này, hãy kiểm tra

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.