2012-01-08 16 views
32

tôi rằng with tuyên bố giúp đỡ bạn để tắt chức năng này:Cách "with" tốt hơn try/catch để mở một tệp bằng Python?

try: 
    f = open(my_file) 
    do_stuff_that_fails() 
except: 
    pass 
finally: 
    f.close() 

Into:

with open(my_file) as f: 
    do_stuff_that_fails() 

Nhưng làm thế nào là tốt hơn? Bạn vẫn phải xử lý trường hợp với tệp không thể mở được (như nhắc người dùng nói với anh ấy rằng anh ấy không có quyền), vì vậy trong thực tế bạn sẽ có:

try: 
    with open(my_file) as f: 
     do_stuff_that_fails() 
except (IOError, OSError, Failure) as e: 
    do_stuff_when_it_doesnt_work() 

Tương đương đến:

try: 
    f = open(my_file) 
    do_stuff_that_fails() 
except (IOError, OSError, Faillure) as e: 
    do_stuff_when_it_doesnt_work() 
finally: 
    f.close() 

Có, bạn đã đạt được hai dòng, nhưng bạn đã thêm mức làm tổ không dễ đọc hơn. Mục đích của câu lệnh with có giúp bạn tiết kiệm hai dòng hoặc tôi thiếu gì đó không?

Có vẻ như rất nhiều để thêm từ khóa chỉ cho điều đó, vì vậy tôi cảm thấy có một số cú pháp để xử lý thử bổ sung/ngoại trừ việc tôi không biết.

+3

"có" sẽ là tên thẻ * khủng khiếp, tùy thuộc vào tình trạng lạm dụng người mới tham gia. Chúng ta có thể tìm thấy cái gì tốt hơn không? – Charles

+0

+1 Điều gì về với từ khóa? –

+0

Python cần một: trywith mở ('bất cứ điều gì', 'r') như f: .... trừ (blah): .... – Will

Trả lời

29

Để bắt đầu, nó giúp ngăn chặn sự cố bạn đã giới thiệu trong ví dụ try ... finally ... của bạn.

Cách bạn đã cấu trúc nó, nếu một ngoại lệ được ném trong khi cố gắng để mở tập tin sau đó bạn sẽ không bao giờ ràng buộc một tập tin mở rộng cho tên f, dẫn đến hoặc là một NameError trong mệnh đề finally (nếu f chưa bao giờ bị ràng buộc trong phạm vi) hoặc một cái gì đó hoàn toàn bất ngờ (nếu nó có).

Cấu trúc chính xác (tương đương với with) là:

f = open(my_file) 

try: 
    do_stuff_that_fails() 
finally: 
    f.close() 

(lưu ý - không cần một khoản except nếu bạn đã có gì để làm ở đó).

ví dụ thứ hai của bạn tương tự là sai, và cần được cấu trúc như:

try: 
    f = open(my_file) 

    try: 
     do_stuff_that_fails() 
    except EXPECTED_EXCEPTION_TYPES as e: 
     do_stuff_when_it_doesnt_work() 
    finally: 
     f.close() 

except (IOError, OSError) as e: 
    do_other_stuff_when_it_we_have_file_IO_problems() 

Thứ hai là (như đã nêu trong câu trả lời khác) mà bạn không thể quên gọi f.close().

BTW, thuật ngữ này là "quản lý bối cảnh", không phải "quản lý tài nguyên" - báo cáo kết quả with quản lý ngữ cảnh, một số trong đó có thể nguồn lực, nhưng những người khác không. Ví dụ: nó cũng được sử dụng với decimal để thiết lập ngữ cảnh thập phân cho một khối mã cụ thể.

Cuối cùng (trả lời nhận xét của bạn cho câu trả lời trước) bạn không bao giờ nên dựa vào ngữ nghĩa refcount để xử lý tài nguyên bằng Python.Jython, IronPython và PyPy đều có ngữ nghĩa không phải trả tiền, và không có gì ngăn chặn CPython đi theo cách khác (mặc dù rất khó cho tương lai trước mắt). Trong một vòng lặp chặt chẽ (ví dụ: os.walk) nó rất rất dễ dàng để chạy ra khỏi tập tin xử lý nếu mã dựa trên ngữ nghĩa refcount được chạy trên một máy ảo với hành vi khác nhau.

+1

+1 Tôi thậm chí không nhận thấy tôi đã mắc phải một sai lầm ngu ngốc như vậy. Cảm ơn bạn đã chỉ ra điều đó. –

6

Đó là quản lý tài nguyên ... không cho cách bạn phản ứng với một ngoại lệ khác :)

Không có cách nào để "quên" f.close() khi sử dụng with. Theo cách này, nó đóng vai trò giống như using trong C#.

Mã hóa vui vẻ.

+0

Vì vậy, nó thực sự tất cả về làm đúng wihtout phải viết nó. Tôi thích quản lý tài nguyên kỳ hạn, điều đó có ý nghĩa. Nhưng tạo một từ khóa mới và một cách suy nghĩ mới về các hoạt động cơ bản để giúp bạn tiết kiệm 'cuối cùng' cảm thấy quá mức cần thiết vì quản lý ressource được tự động xử lý trong destructors gọi là thu gom rác. Trong đó, trừ khi bạn viết các khối khổng lồ hoặc vô số các mã đệ quy, nhanh chóng xảy ra ở cuối hàm hoặc phương thức của bạn. Tôi biết bạn ** nên ** đóng chúng một cách rõ ràng, nhưng nó hoạt động nếu bạn không. Cuối cùng, có bao nhiêu trường hợp sử dụng thực sự được hưởng lợi từ 'with'? –

+0

@ e-satis Các cấu trúc được * không nhất thiết * xác định trong tất cả các triển khai, mặc dù CPython đã dựa trên việc đếm ngược lịch sử (nhưng điều này vẫn có thể thay đổi theo phạm vi biến đổi, với 'with' nó được xử lý * bây giờ *). Tôi không chắc tại sao cú pháp được chọn, nhưng tôi yêu * tính năng này. Có lẽ một cấu trúc 'trywith (...):' bổ sung sẽ gọn gàng. –

+0

Đúng vậy, đó là về việc bị kích thích. Bạn làm đúng, rõ ràng, tự động, ngắn hơn và xác định. Chỉ cần làm cho mọi việc dễ dàng hơn một chút, như trang trí chỉ là đường cú pháp. Chỉ để bình luận đó, tôi sẽ chấp nhận câu trả lời. –

15

Trong ví dụ bạn cung cấp, đó là không phải là tốt hơn. Cách tốt nhất để bắt ngoại lệ là gần với điểm mà chúng được ném để tránh bắt ngoại lệ không liên quan cùng loại.

try: 
    file = open(...) 
except OpenErrors...: 
    # handle open exceptions 
else: 
    try: 
     # do stuff with file 
    finally: 
     file.close() 

Thật không may là như vậy, with statement không cho phép bạn nắm bắt ngoại lệ được ném trong quá trình đánh giá. Có một suggestion thêm xử lý ngoại lệ đối với hiệu ứng này trên mailing list:

with open(...) as file: 
    # do stuff with file 
except OpenErrors...: 
    # handle open exceptions 

Nhưng đây là shot down.

Cuối cùng nó đáng chú ý là bạn có thể trực tiếp nhập và quản lý bối cảnh lối ra như vậy:

file = open(...).__enter__() 
file.__exit__(typ, val, tb) 

này được mô tả chi tiết herehere.

Là hướng dẫn chung, các câu lệnh with vượt trội trong trường hợp ngoại lệ không được mong đợi và hành vi "enter/open/obtain" mặc định là đủ. Ví dụ bao gồm các tệp được yêu cầu và khóa đơn giản.

Các vấn đề liên quan