Trong Python, nếu tôi quay lại bên trong một khối với khối, thì tệp vẫn đóng?


Câu trả lời:


238

Vâng, nó hoạt động giống như finallykhối sau một trykhối, tức là nó luôn luôn thực thi (trừ khi quá trình python kết thúc theo một cách khác thường).

Nó cũng được đề cập trong một trong các ví dụ về PEP-343 là đặc điểm kỹ thuật cho withtuyên bố:

with locked(myLock):
    # Code here executes with myLock held.  The lock is
    # guaranteed to be released when the block is left (even
    # if via return or by an uncaught exception).

Tuy nhiên, một điều đáng nói là bạn không thể dễ dàng bắt gặp các ngoại lệ do open()cuộc gọi ném mà không đặt toàn bộ withkhối bên trong một try..exceptkhối thường không phải là thứ mà người ta muốn.


8
elsecó thể được thêm vào withđể giải quyết try with exceptvấn đề đó . chỉnh sửa: thêm vào ngôn ngữ
rplnt

7
Tôi không biết nó có liên quan hay không, nhưng theo hiểu biết của tôi Process.terminate()là một trong số ít kịch bản (duy nhất?) Không đảm bảo cuộc gọi của một finallytuyên bố: "Lưu ý rằng các trình xử lý thoát và cuối cùng là mệnh đề, v.v., sẽ không Thực thi."
Rik Poggi

@RikPoggi os._exitđôi khi được sử dụng - nó thoát khỏi quy trình Python mà không cần gọi trình xử lý dọn dẹp.
Acumenus

2
Có lẽ chế nhạo con rắn một chút, nhưng điều gì sẽ xảy ra nếu tôi trả lại biểu thức của trình tạo từ bên trong withkhối, liệu bảo đảm có giữ được miễn là trình tạo giữ các giá trị không? miễn là có gì tham khảo nó? Tức là tôi cần sử dụng delhoặc gán một giá trị khác cho biến chứa đối tượng trình tạo?
ack

1
@davidA Sau khi tệp được đóng, các tài liệu tham khảo vẫn có thể truy cập được; tuy nhiên, mọi nỗ lực sử dụng các tham chiếu để kéo / đẩy dữ liệu đến / từ tệp sẽ cho : ValueError: I/O operation on closed file..
RWDJ

36

Đúng.

def example(path, mode):
    with open(path, mode) as f:
        return [line for line in f if condition]

.. nó khá nhiều tương đương với:

def example(path, mode):
    f = open(path, mode)

    try:
        return [line for line in f if condition]
    finally:
        f.close()

Chính xác hơn, __exit__phương thức trong trình quản lý bối cảnh luôn được gọi khi thoát khỏi khối (bất kể ngoại lệ, trả về, v.v.). __exit__Phương thức của đối tượng tệp chỉ gọi f.close()(ví dụ ở đây trong CPython )


30
Một thử nghiệm thú vị để cho thấy sự đảm bảo bạn nhận được từ finallykeywrod là : def test(): try: return True; finally: return False.
Ehsan Kia

20

Đúng. Tổng quát hơn, __exit__phương thức của Trình quản lý bối cảnh với câu lệnh thực sự sẽ được gọi trong trường hợp returntừ bên trong bối cảnh. Điều này có thể được kiểm tra với những điều sau đây:

class MyResource:
    def __enter__(self):
        print('Entering context.')
        return self

    def __exit__(self, *exc):
        print('EXITING context.')

def fun():
    with MyResource():
        print('Returning inside with-statement.')
        return
    print('Returning outside with-statement.')

fun()

Đầu ra là:

Entering context.
Returning inside with-statement.
EXITING context.

Đầu ra ở trên xác nhận rằng __exit__đã được gọi mặc dù sớm return. Như vậy, người quản lý bối cảnh không được bỏ qua.


4

Có, nhưng có thể có một số tác dụng phụ trong các trường hợp khác, bởi vì nó có thể làm một cái gì đó (như bộ đệm xả) trong __exit__khối

import gzip
import io

def test(data):
    out = io.BytesIO()
    with gzip.GzipFile(fileobj=out, mode="wb") as f:
        f.write(data)
        return out.getvalue()

def test1(data):
    out = io.BytesIO()
    with gzip.GzipFile(fileobj=out, mode="wb") as f:
        f.write(data)
    return out.getvalue()

print(test(b"test"), test1(b"test"))

# b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff' b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff+I-.\x01\x00\x0c~\x7f\xd8\x04\x00\x00\x00'
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.