Nhiều biến trong câu lệnh 'với'?


391

Có thể khai báo nhiều hơn một biến bằng cách sử dụng một withcâu lệnh trong Python không?

Cái gì đó như:

from __future__ import with_statement

with open("out.txt","wt"), open("in.txt") as file_out, file_in:
    for line in file_in:
        file_out.write(line)

... hoặc là làm sạch hai tài nguyên cùng một lúc vấn đề?


Có thể như thế này: với [expr1, expr2] là f: và sau đó sử dụng f [0] và f [1].
jbasko

Sẽ tốt hơn vì không cần phải nhập một cái gì đó .... nhưng nó không hoạt động AttributionError: 'list' object không có thuộc tính ' exit '
cá nóc

Nếu con trăn vừa mới đóng cửa, bạn sẽ không cần câu lệnh
BT

Bạn không cần phải sử dụng một tuyên bố, phải không? Bạn chỉ có thể đặt fileFor và file_in thành Không có, sau đó thực hiện thử / ngoại trừ / cuối cùng là nơi bạn mở chúng và xử lý chúng trong thử, rồi cuối cùng đóng chúng nếu chúng không phải là Không có. Không có thụt đôi cần thiết cho điều đó.
M Katz

1
Nhiều câu trả lời trong số này không giải quyết được nhu cầu nhiều hơn hai câu. Về mặt lý thuyết có thể có các ứng dụng cần mở hàng chục bối cảnh, việc lồng nhau tách ra rất nhanh là bất kỳ giới hạn độ dài dòng nào được áp đặt.
ThorSummoner

Câu trả lời:


667

Có thể có trong Python 3 kể từ v3.1Python 2.7 . withCú pháp mới hỗ trợ nhiều trình quản lý bối cảnh:

with A() as a, B() as b, C() as c:
    doSomething(a,b,c)

Không giống như contextlib.nested, điều này đảm bảo rằng absẽ có __exit__()tên gọi của chúng ngay cả khi C()hoặc __enter__()phương thức của nó đưa ra một ngoại lệ.

Bạn cũng có thể sử dụng các biến trước đó trong các định nghĩa sau (h / t Ahmad bên dưới):

with A() as a, B(a) as b, C(a, b) as c:
    doSomething(a, c)

1
có thể đặt các trường bằng với một cái gì đó trong câu lệnh như trong with open('./file') as arg.x = file:không?
Charlie Parker

13
Ngoài ra, có thể: với A () là a, B (a) là b, C (a, b) là c:
Ahmad Yoosofan

kiểm tra lớp2: x = 1; t2 = test2 () với open ('f2.txt') là t2.x: cho l1 trong t2.x.readlines (): print (l1); # Charlie Parker # đã thử nghiệm trên python 3.6
Ahmad Yoosofan

1
xin lưu ý, aslà tùy chọn.
Sławomir Lenart

để làm rõ những gì @ SławomirLenart đang nói: aslà bắt buộc nếu bạn cần đối tượng ahoặc b, nhưng toàn bộ as ahoặc as bkhông bắt buộc
Ciprian Tomoiagă

56

contextlib.nested hỗ trợ này:

import contextlib

with contextlib.nested(open("out.txt","wt"), open("in.txt")) as (file_out, file_in):

   ...

Cập nhật:
Để trích dẫn tài liệu, liên quan đến contextlib.nested:

Không dùng nữa kể từ phiên bản 2.7 : Hiện tại, câu lệnh with hỗ trợ chức năng này trực tiếp (không có các lỗi dễ bị nhầm lẫn).

Xem câu trả lời của Rafał Dowgird để biết thêm thông tin.


34
Tôi rất tiếc phải nói điều đó, nhưng tôi nghĩ rằng trình nestedquản lý bối cảnh là một sai lầm và không bao giờ nên được sử dụng. Trong ví dụ này, nếu việc mở tệp thứ hai sẽ tạo ra một ngoại lệ, thì tệp đầu tiên sẽ hoàn toàn không bị đóng, do đó hoàn toàn phá hủy mục đích sử dụng các trình quản lý bối cảnh.
Rafał Dowgird

tại sao bạn nói như vậy? Tài liệu nói rằng sử dụng lồng nhau tương đương với lồng nhau 'với
James Hopkin

@Rafal: lướt qua hướng dẫn sử dụng dường như chỉ ra rằng python lồng đúng cách với các câu lệnh. Vấn đề thực sự là nếu tệp thứ hai ném ngoại lệ khi đóng.
Không biết

10
@James: Không, mã tương đương trong tài liệu tại docs.python.org/l Library / contentlib.html # contextlib.nested khác với các withkhối lồng nhau tiêu chuẩn . Các trình quản lý được tạo theo thứ tự trước khi nhập các khối: m1, m2, m3 = A (), B (), C () Nếu B () hoặc C () không thành công ngoại lệ, thì bạn chỉ hy vọng hoàn thành đúng A ( ) là người thu gom rác.
Rafał Dowgird

8
Không dùng nữa kể từ phiên bản 2.7 . Lưu ý: Hiện tại, câu lệnh with hỗ trợ chức năng này trực tiếp (không có các quirks dễ bị nhầm lẫn).
miku

36

Lưu ý rằng nếu bạn chia các biến thành các dòng, bạn phải sử dụng dấu gạch chéo ngược để bọc dòng mới.

with A() as a, \
     B() as b, \
     C() as c:
    doSomething(a,b,c)

Dấu ngoặc đơn không hoạt động, vì Python tạo ra một tuple thay thế.

with (A(),
      B(),
      C()):
    doSomething(a,b,c)

Vì các bộ dữ liệu thiếu một __enter__thuộc tính, bạn sẽ gặp lỗi (không mô tả và không xác định loại lớp):

AttributeError: __enter__

Nếu bạn cố gắng sử dụng astrong ngoặc đơn, Python sẽ bắt lỗi khi phân tích cú pháp:

with (A() as a,
      B() as b,
      C() as c):
    doSomething(a,b,c)

Lỗi cú pháp: cú pháp không hợp lệ

https://bugs.python.org/su12782 dường như có liên quan đến vấn đề này.


16

Tôi nghĩ rằng bạn muốn làm điều này thay vào đó:

from __future__ import with_statement

with open("out.txt","wt") as file_out:
    with open("in.txt") as file_in:
        for line in file_in:
            file_out.write(line)

5
Đó là cách tôi hiện đang làm, nhưng sau đó, việc làm tổ sâu gấp đôi so với tôi muốn (có nghĩa là) nó là ...
cá nóc

Tôi nghĩ rằng đây là cách tiếp cận sạch nhất - bất kỳ cách tiếp cận nào khác sẽ khó đọc hơn. Câu trả lời của Alex Martelli dường như gần với những gì bạn muốn nhưng ít đọc hơn nhiều. Tại sao làm tổ là một mối quan tâm như vậy?
Andrew Hare

7
Không phải là một vấn đề lớn, phải thừa nhận, nhưng, mỗi lần "nhập cái này" (hay còn gọi là "Zen của Python"), "căn hộ tốt hơn lồng nhau" - đó là lý do tại sao chúng tôi đã thêm ngữ cảnh vào thư viện chuẩn. BTW, 3.1 có thể có một cú pháp mới "với A () là a, B () là b:" (bản vá nằm trong, không có tuyên bố nào về BDFL cho đến nay) để được hỗ trợ trực tiếp hơn (rõ ràng là giải pháp thư viện không phải là ' Được coi là hoàn hảo ... nhưng tránh việc lồng không mong muốn chắc chắn là mục tiêu được chia sẻ rộng rãi giữa các nhà phát triển Python cốt lõi).
Alex Martelli

2
@Alex: Rất đúng nhưng chúng ta cũng phải xem xét rằng "Tính dễ đọc".
Andrew Hare

4
@Andrew: Tôi nghĩ rằng một mức độ thụt lề thể hiện tốt hơn logic dự định của chương trình, đó là "nguyên tử" tạo hai biến và làm sạch chúng sau này với nhau (tôi nhận ra đây thực sự không phải là điều xảy ra). Hãy nghĩ rằng vấn đề ngoại lệ là một công cụ thỏa thuận mặc dù
cá nóc

12

Kể từ Python 3.3, bạn có thể sử dụng lớp ExitStacktừ contextlibmô-đun.

Nó có thể quản lý một động số đối tượng nhận biết ngữ cảnh, có nghĩa là nó sẽ chứng minh đặc biệt hữu ích nếu bạn không biết có bao nhiêu dữ liệu mà bạn đang đi để xử lý.

Trường hợp sử dụng chính tắc được đề cập trong tài liệu này đang quản lý một số lượng tệp động.

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # All opened files will automatically be closed at the end of
    # the with statement, even if attempts to open files later
    # in the list raise an exception

Đây là một ví dụ chung:

from contextlib import ExitStack

class X:
    num = 1

    def __init__(self):
        self.num = X.num
        X.num += 1

    def __repr__(self):
        cls = type(self)
        return '{cls.__name__}{self.num}'.format(cls=cls, self=self)

    def __enter__(self):
        print('enter {!r}'.format(self))
        return self.num

    def __exit__(self, exc_type, exc_value, traceback):
        print('exit {!r}'.format(self))
        return True

xs = [X() for _ in range(3)]

with ExitStack() as stack:
    print(stack._exit_callbacks)
    nums = [stack.enter_context(x) for x in xs]
    print(stack._exit_callbacks)
print(stack._exit_callbacks)
print(nums)

Đầu ra:

deque([])
enter X1
enter X2
enter X3
deque([<function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86158>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f861e0>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86268>])
exit X3
exit X2
exit X1
deque([])
[1, 2, 3]

0

Trong Python 3.1+, bạn có thể chỉ định nhiều biểu thức ngữ cảnh và chúng sẽ được xử lý như thể nhiều withcâu lệnh đượ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

Điều này cũng có nghĩa là bạn có thể sử dụng bí danh từ biểu thức đầu tiên trong biểu thức thứ hai (hữu ích khi làm việc với các kết nối / con trỏ db):

with get_conn() as conn, conn.cursor() as cursor:
    cursor.execute(sql)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.