2009-01-01 30 views
48

Gần đây tôi đã tự dạy Python và phát hiện thành phần LBYL/EAFP liên quan đến kiểm tra lỗi trước khi thực thi mã. Trong Python, có vẻ như phong cách được chấp nhận là EAFP, và nó có vẻ hoạt động tốt với ngôn ngữ.LBYL vs EAFP trong Java?

LBYL (L ook B efore Y ou L eap):

def safe_divide_1(x, y): 
    if y == 0: 
     print "Divide-by-0 attempt detected" 
     return None 
    else: 
     return x/y 

EAFP (E Asier để Một sk F orgiveness hơn P ermission):

def safe_divide_2(x, y): 
    try: 
     return x/y 
    except ZeroDivisionError: 
     print "Divide-by-0 attempt detected" 
     return None 

Câu hỏi của tôi là thế này: tôi đã thậm chí không bao giờ nghe nói về sử dụng EAFP như xây dựng xác nhận dữ liệu tiểu học, đến từ một nền tảng Java và C++. EAFP là một cái gì đó là khôn ngoan để sử dụng trong Java? Hoặc có quá nhiều chi phí từ trường hợp ngoại lệ? Tôi biết rằng chỉ có chi phí khi một ngoại lệ thực sự bị ném, vì vậy tôi không chắc chắn là tại sao phương pháp EAFP đơn giản hơn không được sử dụng. Nó chỉ là sở thích?

Trả lời

5

Cá nhân, và tôi nghĩ rằng điều này được sao lưu theo quy ước, EAFP không bao giờ là một cách tốt để đi. Bạn có thể nhìn vào nó như là một tương đương như sau:

if (o != null) 
    o.doSomething(); 
else 
    // handle 

như trái ngược với:

try { 
    o.doSomething() 
} 
catch (NullPointerException npe) { 
    // handle 
} 

Hơn nữa, hãy xem xét những điều sau đây:

if (a != null) 
    if (b != null) 
     if (c != null) 
      a.getB().getC().doSomething(); 
     else 
      // handle c null 
    else 
     // handle b null 
else 
    // handle a null 

này có thể trông ít hơn rất nhiều thanh lịch (và có đây là một ví dụ thô - chịu với tôi), nhưng nó mang lại cho bạn nhiều chi tiết hơn trong việc xử lý lỗi, trái ngược với gói nó tất cả trong một try-catch để có được rằng NullPointerException, và sau đó cố gắng tìm ra nơi và tại sao bạn nhận được nó.

Cách tôi thấy EAFP không bao giờ được sử dụng, ngoại trừ những trường hợp hiếm gặp. Ngoài ra, kể từ khi bạn nêu ra vấn đề: có, khối try-catch không phải chịu một số chi phí ngay cả khi ngoại lệ không được ném.

+32

Loại EAFP này phụ thuộc một phần vào việc các trường hợp ngoại lệ bạn kiểm tra có xảy ra thường xuyên hay không. Nếu họ không chắc, thì EAFP là hợp lý. Nếu chúng là phổ biến, thì LBYL có thể tốt hơn. Câu trả lời có lẽ cũng phụ thuộc vào mô hình xử lý ngoại lệ có sẵn. Trong C, LBYL là cần thiết. –

+5

Tôi đồng ý với Jonathan. Trong Python, EAFP là một cách tốt để đi. – Jeff

+8

"ngoại lệ chung" không phải là ngoại lệ, vì vậy tất nhiên LBYL sẽ được ưu tiên hơn trong trường hợp đó, bạn có nghĩ vậy không? –

10

Ngoại lệ được xử lý hiệu quả hơn bằng Python so với Java, ít nhất là một phần lý do bạn thấy cấu trúc đó bằng Python. Trong Java, nó không hiệu quả (về hiệu năng) để sử dụng các ngoại lệ theo cách đó.

+2

mipadi, bạn có có bất kỳ cái nhìn sâu sắc như thế nào python thực hiện điều này? – duffymo

+3

@duffymo Tôi đã có cùng một câu hỏi, và tìm thấy nó ở đây: http://stackoverflow.com/questions/598157/cheap-exception-handling-in-python –

+2

@duffymo hầu hết các trung tâm thảo luận xung quanh LBYL vs EAFP, nhưng một trong số các câu trả lời được liên kết với cách ngoại lệ thực sự được triển khai trong CPython: http://docs.python.org/c-api/intro.html#exceptions –

109

Nếu bạn đang truy cập tệp, EAFP đáng tin cậy hơn LBYL, vì các thao tác liên quan đến LBYL không phải là nguyên tử và hệ thống tệp có thể thay đổi giữa thời gian bạn xem và thời gian bạn nhảy. Trên thực tế, tên chuẩn là TOCTOU - Thời gian kiểm tra, Thời gian sử dụng; lỗi do kiểm tra không chính xác là lỗi TOCTOU.

Xem xét tạo tệp tạm thời phải có tên duy nhất. Cách tốt nhất để tìm hiểu xem tên tệp đã chọn có tồn tại hay không là cố gắng tạo nó - đảm bảo bạn sử dụng các tùy chọn để đảm bảo rằng thao tác của bạn không thành công nếu tệp đã tồn tại (trong điều khoản POSIX/Unix, cờ O_EXCL đến open()).Nếu bạn thử kiểm tra xem tệp đã tồn tại chưa (có thể sử dụng access()), sau đó trong khoảng thời gian khi nói "Không" và thời gian bạn cố gắng tạo tệp, ai đó hoặc một người nào khác có thể đã tạo tệp.

Ngược lại, giả sử bạn cố gắng đọc tệp hiện có. Kiểm tra của bạn rằng tệp tồn tại (LBYL) có thể nói "nó ở đó", nhưng khi bạn thực sự mở nó, bạn thấy "nó không có ở đó".

Trong cả hai trường hợp này, bạn phải kiểm tra thao tác cuối cùng - và LBYL không tự động trợ giúp.

(Nếu bạn đang rối tung với các chương trình SUID hoặc SGID, access() hỏi một câu hỏi khác nhau;. Nó có thể liên quan đến LBYL, nhưng mã vẫn phải đưa vào tài khoản khả năng thất bại)

+3

Ví dụ tuyệt vời, Jonathan. Điều này có thể làm cho rất nhiều ý nghĩa cho các nhà phát triển java đã xử lý với lập trình đồng thời và thành ngữ khóa đôi kiểm tra. –

44

Ngoài chi phí tương đối của các ngoại lệ trong Python và Java, hãy nhớ rằng có sự khác biệt về triết lý/thái độ giữa chúng. Java cố gắng rất nghiêm ngặt về các loại (và mọi thứ khác), đòi hỏi khai báo chi tiết, rõ ràng về các chữ ký lớp/phương thức. Nó giả định rằng bạn nên biết, bất cứ lúc nào, chính xác loại đối tượng bạn đang sử dụng và những gì nó có khả năng làm. Ngược lại, "gõ vịt" của Python có nghĩa là bạn không biết chắc chắn (và không nên quan tâm) loại biểu hiện của một đối tượng là gì, bạn chỉ cần quan tâm rằng nó bỏ qua khi bạn yêu cầu nó. Trong môi trường dễ chấp nhận này, thái độ lành mạnh duy nhất là cho rằng mọi thứ sẽ hoạt động, nhưng sẵn sàng đối phó với những hậu quả nếu họ không làm. Giới hạn tự nhiên của Java không phù hợp với cách tiếp cận bình thường như vậy. (Điều này không có ý định làm gián đoạn cách tiếp cận hoặc ngôn ngữ, mà là để nói rằng thái độ này là một phần của thành ngữ của từng ngôn ngữ, và sao chép thành ngữ giữa các ngôn ngữ khác nhau thường có thể dẫn đến sự lúng túng và giao tiếp kém ...)

+5

Ngoài ra, http://oranlooney.com/lbyl-vs-eafp/ cung cấp một tập hợp các ưu và nhược điểm cho mỗi cách tiếp cận. –

+0

Tôi không đồng ý rằng việc đánh máy lỏng lẻo trong Python khiến cho việc sử dụng EAFP trở nên ít hoặc hợp lý. Khi bạn yêu cầu sự tha thứ, bạn yêu cầu được tha thứ cho các trường hợp dự kiến. Ví dụ, nếu một phương thức "hủy bỏ" sẽ hủy một đối tượng, chúng ta sẽ bắt giữ các ngoại lệ mà chúng ta mong đợi sẽ quay lại. Chúng tôi hy vọng việc hủy có thể không thành công vì đối tượng có các mối quan hệ đang hoạt động ngăn không cho nó bị hủy và chúng tôi muốn liên lạc lại với giao diện người dùng. Nếu phương thức hủy không thành công do một số phân chia không xác định bởi zero scenerio, chúng tôi muốn nó thất bại bình thường như bất kỳ lỗi nào khác trong chương trình. –

1

đoạn mã:

def int_or_default(x, default=0): 
    if x.isdigit(): 
     return int(x) 
    else: 
     return default 

def int_or_default(x, default=0): 
    try: 
     return int(x) 
    except ValueError: 
     return default 

Cả hai đều chính xác, đúng không? Nhưng một trong số đó thì không.

Cũ, sử dụng LBYL, không thành công do sự phân biệt tinh tế giữa isdigitisdecimal; khi được gọi với chuỗi "①²³₅", nó sẽ ném một lỗi thay vì trả về giá trị mặc định một cách chính xác.

Sau này, sử dụng EAFTP, kết quả xử lý chính xác, theo định nghĩa. Không có phạm vi cho hành vi không khớp, vì mã cần yêu cầu mã xác nhận yêu cầu đó.

Sử dụng LBYL nghĩa là lấy logic nội bộ và sao chép chúng vào mọi trang web gọi. Thay vì có một mã hóa kinh điển theo yêu cầu của bạn, bạn sẽ có cơ hội miễn phí để làm rối tung mọi lần bạn gọi hàm.

Cần lưu ý rằng EAFTP không phải là về ngoại lệ và mã Java đặc biệt không nên sử dụng ngoại lệ phổ biến. Đó là về việc cung cấp đúng công việc cho đúng khối mã. Ví dụ, sử dụng giá trị trả về Optional là cách hoàn toàn hợp lệ để viết mã EAFTP và hiệu quả hơn rất nhiều để đảm bảo tính chính xác hơn LBYL.