2013-06-11 36 views
17

tôi thấy this question, và tôi hiểu khi bạn muốn sử dụng with foo() as bar:, nhưng tôi không hiểu khi bạn sẽ chỉ muốn làm:Khi nào thì sử dụng "với" trong python

bar = foo() 
with bar: 
    .... 

Liệu không mà chỉ cần loại bỏ các lợi ích xé rách khi sử dụng with ... as, hoặc tôi có hiểu lầm những gì đang xảy ra không? Tại sao một người nào đó muốn sử dụng chỉ with?

+0

Có thể bạn không muốn thực hiện 'với foo:' trong trường hợp _this_. 'foo' là một cuộc gọi và kết quả của cuộc gọi đó gần như chắc chắn sẽ là trình quản lý ngữ cảnh mà bạn muốn thoát, chứ không phải là chính nó. Nhưng 'với bar:' hoàn toàn hợp lý ở đây, như trong câu trả lời của @ freakish. – abarnert

+0

Bạn đúng; Tôi đã thay đổi nó. – fvrghl

Trả lời

10

Để mở rộng một chút về câu trả lời của @ freakish, with đảm bảo mục nhập vào và sau đó thoát khỏi "ngữ cảnh". Cái quái gì là một bối cảnh? Vâng, đó là "bất cứ điều gì bạn đang làm với nó làm cho nó". Một số hiển nhiên là:

  • khóa: bạn lấy khóa, thao tác một số dữ liệu và nhả khóa.
  • tệp/luồng bên ngoài: bạn mở tệp, đọc hoặc viết và đóng tệp.
  • hồ sơ cơ sở dữ liệu: bạn tìm thấy một bản ghi (thường khóa nó), sử dụng một số trường và có thể thay đổi chúng và phát hành bản ghi (cũng mở khóa).

Ít rõ ràng hơn thậm chí có thể bao gồm một số loại bẫy ngoại lệ nhất định: bạn có thể bắt bằng cách chia số không, thực hiện một số phép tính, và sau đó ngừng bắt nó. Tất nhiên điều đó được xây dựng trong cú pháp Python: try ... except như một khối! Và, trên thực tế, with đơn giản là trường hợp đặc biệt của các cơ chế thử/ngoại lệ/cuối cùng của Python (về mặt kỹ thuật, try/finally được bao quanh một khối try khác; xem nhận xét).

Phần as của khối with hữu ích khi mục nhập ngữ cảnh cung cấp một số giá trị mà bạn muốn sử dụng bên trong khối. Trong trường hợp của tệp hoặc bản ghi cơ sở dữ liệu, rõ ràng là bạn cần luồng mới được mở hoặc bản ghi chỉ có được. Trong trường hợp bắt ngoại lệ, hoặc giữ một khóa trên cấu trúc dữ liệu, có thể không cần phải lấy giá trị từ ngữ cảnh.

+1

'with' không thực sự là một trường hợp đặc biệt của' try'/'except', hoặc là khái niệm hoặc trong thực tế. Nó gần hơn với một trường hợp đặc biệt của 'try' /' final', nhưng ngay cả điều đó cũng không thực sự chính xác. (Nó thực sự là 'try' /' except'/'else' bên trong' try'/'final', với ít nhất hai lời gọi rõ ràng tới' __exit__' ở những vị trí khác nhau.) – abarnert

+0

@abarnert: true; Tôi đã cố gắng đơn giản hóa và có thể quá đơn giản.:-) – torek

+0

Nhưng tôi nghĩ rằng đó là một chút gây hiểu lầm theo cách này. Đó là 'cuối cùng' đó là phần quan trọng đối với khối 'with' trực giác, không phải là' ngoại trừ'. (BTW, tôi muốn bất cứ ai downvoted điều này sẽ thêm bình luận, bởi vì tôi không nghĩ bình luận của tôi là đủ để biện minh cho bầu cử, và nếu có vấn đề khác thì nó thực sự nâng cao nó để câu trả lời có thể được cải thiện, thay vì lái xe -by không hài lòng bỏ phiếu.) – abarnert

5

Ví dụ khi bạn muốn sử dụng Lock():

from threading import Lock 
myLock = Lock() 
with myLock: 
    ... 

Bạn không thực sự cần đối tượng Lock(). Bạn chỉ cần biết rằng nó là trên.

+0

Tôi nghĩ bạn có nghĩa là "Bạn không thực sự cần đối tượng Khóa." – martineau

+0

@martineau: Có thể có chút rắc rối, nhưng tôi không nghĩ nó sai hoặc gây nhầm lẫn. "Đối tượng' Khóa() 'có nghĩa là gì nhưng" đối tượng được trả về bởi lệnh 'Khóa()'? – abarnert

+0

@abarnert: Chính xác, bạn _do_ cần đối tượng trả về cho 'with', không phải chính đối tượng' Lock' (lớp). Đó là cách tôi nghĩ những gì đã nói là sai. – martineau

4

Sử dụng with mà không cần as vẫn mang đến cho bạn chính xác teardown; nó không giúp bạn có được một đối tượng địa phương mới đại diện cho bối cảnh.

Lý do bạn muốn điều này là đôi khi chính bối cảnh không trực tiếp hữu ích - nói cách khác, bạn chỉ sử dụng nó cho các hiệu ứng phụ của bối cảnh của nó nhập và thoát. Ví dụ, với một đối tượng Lock, bạn phải có đối tượng cho khối with là hữu ích - vì vậy, ngay cả khi bạn cần nó trong khối, không có lý do gì để rebind nó vào một tên khác. Quay trở lại đầu trang Điều này cũng đúng khi bạn sử dụng contextlib.closing trên một đối tượng không phải là người quản lý ngữ cảnh — bạn đã có chính đối tượng, vì vậy ai quan tâm đến sản lượng closing?

Với thứ gì đó như sh.sudo, thậm chí không có đối tượng mà bạn muốn sử dụng trong bất kỳ khoảng thời gian nào.

Ngoài ra còn có các trường hợp mà điểm của người quản lý ngữ cảnh ở đó để lưu trữ và tự động khôi phục một số trạng thái. Ví dụ: bạn có thể muốn viết một termios.tcsetattr -stasher, vì vậy bạn có thể gọi tty.setraw() bên trong khối. Bạn không quan tâm đối tượng stash trông như thế nào, tất cả những gì bạn quan tâm là nó được khôi phục tự động.

decimal.localcontext có thể hoạt động theo bất kỳ cách nào - bạn có thể truyền cho đối tượng bạn đã có (và do đó không cần tên mới) hoặc chuyển cho đối tượng tạm thời chưa được đặt tên hoặc chỉ lưu trữ bối cảnh được khôi phục tự động. Nhưng trong bất kỳ trường hợp nào.

Có một số trường hợp lai đôi khi bạn muốn ngữ cảnh, đôi khi bạn không muốn. Ví dụ, nếu bạn chỉ muốn một giao dịch cơ sở dữ liệu để tự động cam kết, bạn có thể viết with autocommit(db.begin()):, bởi vì bạn sẽ không truy cập nó bên trong khối. Nhưng nếu bạn muốn tự động khôi phục trừ khi bạn cam kết rõ ràng, bạn có thể viết with autorollback(db.begin()) as trans:, vì vậy bạn có thể bên trong khối. (Tất nhiên thường xuyên, bạn sẽ thực sự muốn có một giao dịch mà cam kết về xuất cảnh bình thường và cuộn lại ngoại lệ, như trong ví dụ transactionPEP 343 's. Nhưng tôi không thể nghĩ ra một ví dụ lai tốt hơn ở đây ...)

PEP 343 và những người tiền nhiệm của nó (PEP 310, PEP 340, và những thứ khác được liên kết từ 343) giải thích tất cả điều này ở một mức độ nào đó, nhưng có thể hiểu được rằng bạn sẽ không đọc nó một cách bình thường - có rất nhiều thông tin không liên quan và chủ yếu chỉ giải thích tổng quan về dặm cao và sau đó là chi tiết cấp độ triển khai, bỏ qua mọi thứ ở giữa.

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