ใน Python ถ้าฉันกลับเข้าไปในบล็อก“ with” ไฟล์จะยังคงปิดหรือไม่


คำตอบ:


238

ใช่มันทำหน้าที่เหมือนfinallyบล็อกหลังtryบล็อกนั่นคือมันจะดำเนินการเสมอ (เว้นแต่ว่ากระบวนการของไพ ธ อนจะยุติลงด้วยวิธีที่ผิดปกติ)

มันถูกกล่าวถึงในหนึ่งในตัวอย่างของPEP-343ซึ่งเป็นข้อกำหนดสำหรับwithคำสั่ง:

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).

อย่างไรก็ตามสิ่งที่ควรค่าแก่การกล่าวถึงคือคุณไม่สามารถจับข้อยกเว้นที่เกิดขึ้นจากการopen()โทรได้โดยง่ายโดยไม่ต้องวางwithบล็อกทั้งหมดไว้ในtry..exceptบล็อกซึ่งโดยปกติจะไม่ใช่สิ่งที่ต้องการ


8
elseสามารถเพิ่มwithเพื่อแก้ไขtry with exceptปัญหานั้นได้ แก้ไข: เพิ่มในภาษา
rplnt

7
ฉันไม่รู้ว่ามันเกี่ยวข้องหรือไม่ แต่สำหรับความรู้ของฉันProcess.terminate()เป็นหนึ่งในไม่กี่สถานการณ์ (เท่านั้น) ที่ไม่รับประกันการเรียกใช้finallyคำสั่ง: "โปรดทราบว่าตัวจัดการทางออกและส่วนท้าย ฯลฯ จะไม่เป็นเช่นนั้น ดำเนินการ."
Rik Poggi

@RikPoggi os._exitบางครั้งใช้ - มันออกจากกระบวนการ Python โดยไม่เรียกตัวจัดการการล้าง
คิวเมนตัส

2
บางทีการเยาะเย้ยงูนิดหน่อย แต่ถ้าฉันคืนการแสดงออกของตัวกำเนิดจากภายในwithบล็อกการรับประกันจะเก็บไว้ตราบใดที่เครื่องกำเนิดยังคงให้ค่าผล? ตราบใดที่มีการอ้างอิงอะไร คือฉันจำเป็นต้องใช้delหรือกำหนดค่าที่แตกต่างให้กับตัวแปรที่เก็บวัตถุตัวสร้าง?
แอ๊กชั่น

1
@davidA หลังจากปิดไฟล์แล้วการอ้างอิงยังคงสามารถเข้าถึงได้ แต่ความพยายามใด ๆ ที่จะใช้อ้างอิงถึงข้อมูลที่ดึง / ดัน / ValueError: I/O operation on closed file.จากไฟล์ที่จะทำให้:
RWDJ

36

ใช่.

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

.. มันเทียบเท่ากับ:

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

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

แม่นยำยิ่งขึ้น__exit__วิธีการในตัวจัดการบริบทจะถูกเรียกเสมอเมื่อออกจากบล็อก (ไม่ว่าจะมีข้อยกเว้นส่งคืน ฯลฯ ) หรือไม่ __exit__วิธีการของวัตถุไฟล์เพียงแค่เรียกf.close()(เช่นที่นี่ใน CPython )


30
การทดลองที่น่าสนใจที่จะแสดงการรับประกันที่คุณได้รับจากfinallykeywrod def test(): try: return True; finally: return Falseคือ:
Ehsan Kia

20

ใช่. โดยทั่วไปแล้ว__exit__วิธีการของWith Context Managerจะถูกเรียกใช้ในกรณีที่มี a returnจากภายในบริบท สิ่งนี้สามารถทดสอบได้ดังต่อไปนี้:

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()

ผลลัพธ์คือ:

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

ผลลัพธ์ข้างต้นยืนยันว่า__exit__ถูกเรียกทั้ง ๆ ที่ก่อนreturnหน้านี้ ดังนั้นตัวจัดการบริบทจะไม่ถูกข้าม


4

ใช่ แต่อาจมีผลข้างเคียงบางอย่างในกรณีอื่น ๆ เพราะอาจทำบางสิ่ง (เช่นการล้างบัฟเฟอร์) ใน__exit__บล็อก

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'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.