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ệ?
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ệ?
Câu trả lời:
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ư ValueError
hoặ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ý.
except:
cũng bắt được (trong số nhiều thứ khác) NameError
và AttributeError
, vì vậy nếu bạn viết sai chính tả một cái gì đó trong try
khối (ví dụ: hàm "insert" của bạn thực sự được gọi insert_one
vì 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()
.
main()
)?
except Exception:
sẽ nắm bắt NameError
và AttributeError
quá. Đ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 exit
hoặc sys.exit
và 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.
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.
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.
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".
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
except Exception:
tránh các loại trên mà chúng ta không muốn mắc phải?
except Exception
Ổn.
except Exception
bắt SyntaxError
và MemoryError
vì 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)
Đây là những nơi tôi sử dụng ngoại trừ không có loại
Đó 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
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
Tôi có hai cách để làm điều đó:
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
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ề".