4

Quand j'écris:générateurs et fichiers

lines = (line.strip() for line in open('a_file')) 

est le fichier ouvert immédiatement ou est le système de fichiers accessible uniquement quand je commence à consommer l'expression du générateur?

+2

Si vous ouvrez 'open = print' en premier, votre code affiche' a_file'. –

+0

@StefanPochmann Il m'a fallu un certain temps, mais au moins j'ai compris votre commentaire ... Merci beaucoup – gboffi

+0

@MSeifert Very nice edit! – gboffi

Répondre

4

open() est appelée immédiatement lors de la construction du générateur, indépendamment de quand ou si vous en consommez.

Les spécifications pertinentes est PEP-289:

liaison anticipée par rapport à liaison tardive

Après beaucoup de discussions, il a été a décidé que la première (la plus externe) pour l'expression doit être évaluée immédiatement et que les expressions restantes soient évaluées lorsque le générateur est exécuté.

Prié de résumer le raisonnement pour lier la première expression, Guido offert [5]:

Tenez compte sum(x for x in foo()). Supposons maintenant qu'il y ait un bogue dans foo() qui déclenche une exception, et un bogue dans sum() qui déclenche une exception avant qu'il ne commence à itérer sur son argument. Quelle exception serait que vous vous attendez à voir? Je serais surpris si celui sum() a été soulevée plutôt celui foo(), puisque l'appel à foo() fait partie de l'argument à sum(), et j'attends des arguments à traiter avant que la fonction est appelée.

OTOH, dans sum(bar(x) for x in foo()), où sum() et foo() sont sans bugs, mais bar() soulève une exception, on n'a pas d'autre choix que de retarder l'appel à bar() jusqu'à sum() commence itérer - qui fait partie du contrat de générateurs. (Ils ne font rien jusqu'à ce que leur méthode est next() premier appelé.)

Voir le reste de cette section pour poursuivre la discussion.

5

Il est ouvert immédiatement. Vous pouvez le vérifier si vous utilisez un nom de fichier qui n'est pas présent (il lancera une exception qui indique que Python a effectivement essayé de l'ouvrir immédiatement).

Vous pouvez également utiliser une fonction qui donne plus de commentaires pour voir que la commande est exécutée avant même que le générateur est itéré:

def somefunction(filename): 
    print(filename) 
    return open(filename) 

lines = (line.strip() for line in somefunction('a_file')) # prints 

Toutefois, si vous utilisez une fonction de générateur au lieu d'une expression du générateur le fichier est ouvert uniquement lorsque vous le parcourez:

def somefunction(filename): 
    print(filename) 
    for line in open(filename): 
     yield line.strip() 

lines = somefunction('a_file') # no print! 

list(lines)      # prints because list iterates over the generator function. 
+0

J'apprécie votre observation sur la différence entre les expressions de générateur et les fonctions du générateur. Je n'ai pas approuvé votre réponse parce que je préfère [la] (https://stackoverflow.com/a/45758642/2749397) référençant le processus de décision original, mais le vôtre est un très bon! – gboffi

1

Il est ouvert immédiatement.

Exemple:

def func(): 
    print('x') 
    return [1, 2, 3] 

g = (x for x in func()) 

sortie:

x 

La fonction doit renvoyer un objet iterable. open() renvoie un objet fichier ouvert qui peut être itéré. Par conséquent, le fichier sera ouvert lorsque vous définissez l'expression du générateur.