web-dev-qa-db-fra.com

En Python, si je retourne dans un bloc "avec", le fichier sera-t-il toujours fermé?

Considérer ce qui suit:

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

Le fichier sera-t-il fermé correctement ou l'utilisation de return contourne-t-elle en quelque sorte le gestionnaire de contexte ?

207
Lightbreeze

Oui, il agit comme le bloc finally après un bloc try, c’est-à-dire qu’il est toujours exécuté (sauf si le processus python se termine de façon inhabituelle).

Il est également mentionné dans l'un des exemples de PEP-34 , qui est la spécification de l'instruction 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).

Il convient toutefois de mentionner qu’il est difficile d’attraper les exceptions émises par l’appel open() sans placer le bloc entier with dans un bloc try..except, Ce qui n’est généralement pas ce que l’on veut. .

186
ThiefMaster

Oui.

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

..est à peu près équivalent à:

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

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

Plus précisément, la méthode __exit__ Dans un gestionnaire de contexte est toujours appelée lors de la sortie du bloc (quelles que soient les exceptions, les retours, etc.). La méthode __exit__ De l'objet fichier appelle simplement f.close() (par exemple ici en CPython )

29
dbr

Oui. Plus généralement, le __exit__ La méthode d'un With Statement Context Manager sera en effet appelée dans le cas d'un return de l'intérieur du contexte. Cela peut être testé avec les éléments suivants:

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

La sortie est:

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

La sortie ci-dessus confirme que __exit__ a été appelé malgré le début return. En tant que tel, le gestionnaire de contexte n'est pas ignoré.

15
Acumenus

Oui, mais il peut y avoir un effet secondaire dans d'autres cas, car il peut être utile de faire quelque chose (comme vider le tampon) dans __exit__ bloc

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'
3
virusdefender