2015-06-19 15 views
5

Nel seguente codice mi aspetto che python stia liberando fileinput.input quando sono al numero return nel mezzo del mio ciclo perché sta andando fuori campo. Tuttavia, quando si chiama di nuovo la mia funzione fileinput mi dicePerché l'oggetto fileinput.input non viene perso quando si esce dall'ambito di applicazione?

raise RuntimeError, "input() already active" 

Ecco il mio codice:

def func(inplace): 
    for line in fileinput.input(sys.argv[1], inplace=inplace): 
     [..] 
     if condition: 
      return True 
     [..] 

if func(False): 
    func(True) 

mi aspetterei questo comportamento quando si utilizza yield ma non quando si utilizza il ritorno.

Sto usando Python 2.7.3.

C'è un modo per forzare un reset di fileinput?

EDIT:

Quando si chiama fileinput.close() prima di tornare funziona. Perché non è fatto implicitamente?

EDIT 2: Grazie a @MatsLindh

Sostituzione

for line in fileinput.input(sys.argv[1], inplace=inplace): 

con

for line in fileinput.FileInput(sys.argv[1], inplace=inplace): 

fa quello che voglio, perché restituisce un oggetto che va out-of-scope in un modo definito. Ho pensato che lo fileinput.input() lo facesse, ma no. Sta usando un'istanza globale.

+0

Perché ti aspetti questo? Python non garantisce la pulizia al blocco (è solo cpython che di solito lo fa, ma solo quando non ci sono cicli di riferimento). – dhke

+0

La raccolta dei dati inutili può verificarsi in qualsiasi momento; e ci sono le condizioni in cui gli oggetti vengono liberati e puliti. Se vuoi che l'oggetto file venga chiuso quando esci dal blocco usa '' with'' –

+1

@JamesMills Penso che dovresti rendere il tuo commento una risposta. Sembra essere questo, IMHO. –

risposta

2

Qui non c'è nulla che possa andare fuori portata - si sta chiamando una funzione in un modulo che è stato importato e fileinput è uno di quei moduli che ha stato globale, dal momento che i futuri inviti a fileinput mappe a la chiamata input effettuata in precedenza.

Si dovrebbe essere in grado di ovviare a questo utilizzando la classe FileInput invece, che sarà ricreata la prossima volta che si chiama func() e utilizzando with insieme a quell'oggetto.

Oppure - come hai scoperto, chiamando close() che ripristina lo stato del modulo interno.

+0

Penso che tu lo stia inchiodando. La mia ipotesi era che fileinput.input() restituisca un oggetto. Che esce dal campo di applicazione quando si lascia il ciclo. –

3

Così si necessità di avvolgere fileinput.input() in un "contesto manager" closing che ** garantire * 8 che .close() si chiama quando si esce dal blocco utilizzando with ...::

from contextlib import closing 

def func(inplace): 
    with closing(fileinput.input(sys.argv[1], inplace=inplace)) as finput: 
     for line in finput: 
      [..] 
      if condition: 
       return True 
      [..] 

attraverso un gestore di contesto sugli oggetti che implementano il protocollo; di solito gli oggetti file e simili, , assicurano che le operazioni pulite vengano eseguite all'uscita del blocco with.


Garbage Collection può verificarsi in qualsiasi momento; e ci sono le condizioni in cui gli oggetti vengono liberati e puliti. Se si desidera chiudere l'oggetto file quando si esce dal blocco, utilizzare with.

+3

'fileinput' è un [modulo standard python] (https://docs.python.org/2/library/fileinput.html) che mantiene lo stato globale della precedente chiamata a' input() 'per chiamate future ad altri fileinput funzioni. – MatsLindh

+0

Sì; Esattamente; La cosa ** chiave ** qui è usare un "gestore di contesto". –

+0

'FileInput' (cosa viene restituito da' input() ') non implementa' __exit __() ', da qui la necessità di' closing() ' – dhke

2

Quando si chiama fileinput.close() prima di restituirlo funziona. Perché non è fatto implicitamente?

Because: L'esplicito è meglio che implicito.

Python non garantisce in alcun modo la semantica di pulizia dei blocchi, ovvero una risorsa non viene necessariamente ripulita immediatamente quando esce dall'ambito. L'implementazione CPython utilizza il conteggio dei riferimenti conservativi in ​​modo che possa sembrare che questo sia il caso. Non lo è. Ecco perché c'è il anche la raccolta dei dati inutili ciclica.

Se si vuole pulizia, farlo in modo esplicito:

from contextlib import closing 

def func(inplace): 
    with closing(fileinput.input(sys.argv[1], inplace=inplace)) as finput: 
     for line in finput: 
      [..] 
     if condition: 
      return True 
     [..] 

è necessario il closing() involucro perché FileInput oggetti --at almeno sulla pitone 2.7-- non sono responsabili di contesto, vale a dire non c'è nessun metodo __exit__().

+0

Vorrei estendere la frase: * Python non garantisce in alcun modo la semantica di pulizia dei blocchi * con * tranne se la stai usando correttamente. * –

2

Gli oggetti simili a file non vengono chiusi implicitamente. Inoltre, FileInput non supporta l'istruzione with.

Tuttavia, è possibile utilizzare il gestore di closing contesto:

from contextlib import closing 

with closing(fileinput.input(inplace=inplace)) as input: 
    for line in input: 
     if condition: 
      return True 

ho rimosso l'argomento files=sys.argv[1] per brevità e come predefinito è sys.argv[1:]. Presumo che questo farà ancora quello che vuoi.

Problemi correlati