Python'da, bir "with" bloğunun içine geri dönersem, dosya yine de kapanır mı?


Yanıtlar:


238

Evet, bir finallybloktan sonra blok gibi davranır try, yani her zaman yürütülür (python işlemi alışılmadık bir şekilde sona ermediği sürece).

Ayrıca, açıklama için şartname olan PEP-343'ün örneklerinden birinde belirtilmiştir 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).

Bununla birlikte, kayda değer bir şey open(), tüm withbloğu bir try..exceptbloğun içine koymadan çağrı tarafından atılan istisnaları kolayca yakalayamayacağınızdır ;


8
elsewithbu try with exceptsorunu çözmek için eklenebilir . düzenleme: dile eklendi
rplnt

7
Alakalı olup olmadığını bilmiyorum, ancak bildiklerime Process.terminate()göre bir finallyifadenin çağrısını garanti etmeyen birkaç (tek?) Senaryodan biri : "Çıkış işleyicileri ve nihayet hükümler, vb. idam."
Rik Poggi

@RikPoggi os._exitbazen kullanılır - temizleme işleyicilerini çağırmadan Python işleminden çıkar.
Acumenus

2
Belki de yılanı biraz alay ediyorlar, ama withblok içinde bir jeneratör ifadesi döndürürsem , jeneratör değerleri vermeye devam ettiği sürece garanti geçerli olur mu? bir şey referans verdiği sürece? Yani deljeneratör nesnesini tutan değişkene farklı bir değer kullanmalı mıyım?
ack

1
@davidA Dosya kapatıldıktan sonra referanslara hala erişilebilir; ancak, herhangi bir girişimleri için kullanmak verecek dosyadan / konumuna çekme / itme verilere başvurular: ValueError: I/O operation on closed file..
RWDJ

36

Evet.

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

.. hemen hemen eşdeğerdir:

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

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

Daha doğrusu, __exit__içerik yöneticisindeki yöntem her zaman bloktan çıkılırken çağrılır (istisnalar, iadeler vb. Ne olursa olsun). Dosya nesnesinin __exit__yöntemi sadece çağırıyor f.close()(örneğin, burada CPython'da )


30
Eğer aldığım garanti göstermek için ilginç bir deney finallykeywrod geçerli: def test(): try: return True; finally: return False.
Ehsan Kia

20

Evet. Daha genel olarak, Deyim İçeren Bağlam Yöneticisinin__exit__ yöntemi gerçekten bağlam içinden bir durumunda çağrılır . Bu aşağıdakilerle test edilebilir: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()

Çıktı:

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

Yukarıdaki çıktı __exit__, erken olmasına rağmen çağrıldığını doğrulamaktadır return. Bu nedenle, bağlam yöneticisi atlanmaz.


4

Evet, ancak diğer durumlarda bazı yan etkiler olabilir, çünkü __exit__blokta bir şey (yıkama tamponu gibi) yapmalıdır.

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'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.