2010-06-11 31 views
117

Giả sử bạn có ba đối tượng bạn có được thông qua trình quản lý ngữ cảnh, ví dụ: Khóa, kết nối db và ổ cắm ip. Bạn có thể mua chúng bằng cách:python: tạo khối "có" trên một số trình quản lý ngữ cảnh

with lock: 
    with db_con: 
     with socket: 
      #do stuff 

Nhưng có một cách để làm điều đó trong một khối? cái gì đó như

with lock,db_con,socket: 
    #do stuff 

Bên cạnh đó, là nó có thể, đưa ra một mảng không rõ chiều dài của đối tượng mà có người quản lý bối cảnh, là nó có thể bằng cách nào đó làm:

a=[lock1, lock2, lock3, db_con1, socket, db_con2] 
with a as res: 
    #now all objects in array are acquired 

Nếu câu trả lời là "không", có phải vì sự cần thiết cho một tính năng như vậy có nghĩa là thiết kế xấu, hoặc có lẽ tôi nên đề nghị nó trong một pep? :-P

+1

có thể trùng lặp của [Nhiều biến trong Python 'bằng' tuyên bố] (http://stackoverflow.com/questions/893333/multiple-variables-in-python-with-statement) –

Trả lời

212

Trong Python 2.6 và dưới đây, bạn có thể sử dụng contextlib.nested:

from contextlib import nested 

with nested(A(), B(), C()) as (X, Y, Z): 
    do_something() 

tương đương với:

m1, m2, m3 = A(), B(), C() 
with m1 as X: 
    with m2 as Y: 
     with m3 as Z: 
      do_something() 

Lưu ý rằng đây không phải là chính xác giống như bình thường sử dụng lồng nhau with, bởi vì A(), B()C() tất cả sẽ được gọi ban đầu, trước khi vào trình quản lý ngữ cảnh. Điều này sẽ không hoạt động chính xác nếu một trong các hàm này có thể tăng ngoại lệ, nhưng sẽ làm việc cho các ví dụ trong câu hỏi.


Trong Python 2.7 và 3.1, cú pháp đã được thêm vào cho điều này, và contextlib.nested đã được chấp nhận:

with A() as X, B() as Y, C() as Z: 
    do_something() 

Trong Python 3.3, bạn cũng có thể nhập một không rõ danh sách độ dài của người quản lý ngữ cảnh bằng cách sử dụng contextlib.ExitStack:

with ExitStack() as stack: 
    for mgr in ctx_managers: 
     stack.enter_context(mgr) 
    # ... 

Điều này cho phép bạn tạo các trình quản lý ngữ cảnh khi bạn thêm chúng vào ExitStack, ngăn sự cố có thể xảy ra với contextlib.nested.

contextlib2 cung cấp a backport of ExitStack cho Python 2.6 và 2,7.

+0

cảm ơn! Vì vậy, tôi có thể sử dụng điều này cho một mảng các trình quản lý ngữ cảnh với 'contextlib.nested (* arr)'.
Điều này có thể bằng cách nào đó trong cú pháp mới của python 2.7 và 3.1 không? – olamundo

+2

@noam: Không, trên thực tế, docstring cho 'lồng nhau' trong 3.1 cho biết:" Một lợi thế của hàm này trên biểu mẫu nhiều người quản lý của câu lệnh with là giải nén đối số cho phép nó được sử dụng với số lượng người quản lý ngữ cảnh như sau: 'với lồng nhau (* người quản lý): do_something()' " – interjay

+9

Odd, một mặt nó không còn được dùng nữa, nhưng mặt khác, họ thừa nhận lợi thế của mô-đun không được dùng nữa thay thế? – olamundo

18

Phần đầu tiên của câu hỏi là có thể trong Python 3.1.

Với nhiều hơn một mục, các nhà quản lý bối cảnh được chế biến như nhiều với báo cáo được lồng nhau:

with A() as a, B() as b: 
    suite 

tương đương với

with A() as a: 
    with B() as b: 
     suite 

Thay đổi trong phiên bản 3.1: Hỗ trợ cho nhiều biểu thức ngữ cảnh

+0

cảm ơn! nhưng điều đó vẫn không trả lời toàn bộ câu hỏi của tôi: về trường hợp thứ 2 tôi đã đề cập, nơi các nhà quản lý ngữ cảnh được đưa ra trong một mảng, mà không biết có bao nhiêu người quản lý có trong mảng đó. nó sẽ có thể trong một số python3.X để làm 'với [cm1, cm2, cm3, cm4, cm5] như là kết quả: ....' – olamundo

+2

@noam: Để giải quyết phần thứ hai của câu hỏi của bạn, bạn có thể viết một lớp để bọc một số tài nguyên và triển khai '__enter__' và' __exit__' cho lớp đó. Tôi không chắc liệu có một lớp thư viện chuẩn nào đã thực hiện điều này chưa. –

+0

@ Mark Tôi không nghĩ rằng điều đó là dễ dàng - đó là lý do tại sao 'contextlib.nested()' bị phản đối. Nếu có điều gì xảy ra giữa việc tạo ra các thứ khác và kích hoạt trình quản lý ngữ cảnh, có thể xảy ra việc dọn dẹp không xảy ra như mong muốn. – glglgl

7

Phần thứ hai của câu hỏi của bạn được giải quyết với contextlib.ExitStack trong Python 3.3.

1

Câu trả lời của @ interjay là chính xác. Tuy nhiên, nếu bạn cần làm điều này cho các nhà quản lý ngữ cảnh dài, ví dụ như các trình quản lý ngữ cảnh mock.patch, thì bạn nhanh chóng nhận ra rằng bạn muốn phá vỡ điều này trên các dòng. Hóa ra bạn không thể quấn chúng trong parens, vì vậy bạn phải sử dụng dấu gạch chéo ngược. Dưới đây là hình thức:

with mock.patch('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') as a, \ 
     mock.patch('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb') as b, \ 
     mock.patch('cccccccccccccccccccccccccccccccccccccccccc') as c: 
    do_something() 
Các vấn đề liên quan