Devo passare i nomi di file da aprire o aprire i file?

45

Supponiamo che abbia una funzione che fa cose con un file di testo - ad esempio legge da esso e rimuove la parola 'a'. Potrei passargli un nome di file e gestire l'apertura / chiusura della funzione, oppure potrei passarlo il file aperto e aspettarmi che chiunque lo chiami si occupi di chiuderlo.

Il primo modo sembra un modo migliore per garantire che nessun file rimanga aperto, ma mi impedisce di usare cose come gli oggetti StringIO

Il secondo modo potrebbe essere un po 'pericoloso - non c'è modo di sapere se il file sarà chiuso o no, ma sarei in grado di usare oggetti simili a file

def ver_1(filename):
    with open(filename, 'r') as f:
        return do_stuff(f)

def ver_2(open_file):
    return do_stuff(open_file)

print ver_1('my_file.txt')

with open('my_file.txt', 'r') as f:
    print ver_2(f)

Uno di questi è generalmente preferito? È generalmente previsto che una funzione si comporterà in uno di questi due modi? O dovrebbe essere solo ben documentato in modo tale che il programmatore possa usare la funzione come appropriato?

    
posta Dannnno 10.11.2014 - 20:09
fonte

3 risposte

33

Le pratiche interfacce sono piacevoli e, a volte, la strada da percorrere. Tuttavia, la maggior parte delle volte la buona componibilità è più importante della praticità , poiché un'astrazione componibile ci consente di implementare altre funzionalità (inclusi i wrapper di convenienza) su di essa.

Il modo più generale per la tua funzione di utilizzare i file è di prendere un handle di file aperto come parametro, in quanto ciò consente di utilizzare anche handle di file che non fanno parte del filesystem (es. pipe, socket, ...):

def your_function(open_file):
    return do_stuff(open_file)

Se l'ortografia di with open(filename, 'r') as f: result = your_function(f) è troppo chiedere ai tuoi utenti, puoi scegliere una delle seguenti soluzioni:

  • your_function prende un file aperto o un nome di file come parametro. Se si tratta di un nome file, il file viene aperto e chiuso e le eccezioni vengono propagate. Qui c'è un problema con l'ambiguità che potrebbe essere aggirato usando argomenti con nome.
  • Offri un semplice wrapper che si occupi di aprire il file, ad es.

    def your_function_filename(file):
        with open(file, 'r') as f:
            return your_function(f)
    

    In genere percepisco funzioni come API bloat, ma se forniscono funzionalità di uso comune, la convenienza acquisita è un argomento sufficientemente strong.

  • Avvolgi la funzionalità with open in un'altra funzione componibile:

    def with_file(filename, callback):
        with open(filename, 'r') as f:
            return callback(f)
    

    usato come with_file(name, your_function) o in casi più complicati with_file(name, lambda f: some_function(1, 2, f, named=4))

risposta data 10.11.2014 - 20:34
fonte
17

La vera domanda è di completezza. La tua elaborazione di file funziona con l'elaborazione completa del file o è solo un pezzo di una catena di passaggi di elaborazione? Se è completo e a sé stante, quindi sentitevi liberi di incapsulare tutti gli accessi ai file all'interno di una funzione.

def ver(filepath):
    with open(filepath, "r") as f:
        # do processing steps on f
        return result

Questo ha la proprietà molto bella di finalizzare la risorsa (chiudendo il file) alla fine dell'istruzione with .

Se tuttavia è possibile che sia necessario elaborare un file già aperto, la distinzione tra ver_1 e ver_2 ha più senso. Ad esempio:

def _ver_file(f):
    # do processing steps on f
    return result

def ver(fileobj):
    if isinstance(fileobj, str):
        with open(fileobj, 'r') as f:
            return _ver_file(f)
    else:
        return _ver_file(fileobj)

Questo tipo di test di tipo esplicito è spesso disapprovato , specialmente in linguaggi come Java, Julia e Go, dove il dispacciamento basato su tipo o interfaccia è direttamente supportato. In Python, tuttavia, non esiste supporto linguistico per la distribuzione basata su tipi. Di tanto in tanto potresti riscontrare critiche nei test di tipo diretto in Python, ma in pratica è estremamente comune e abbastanza efficace. Permette a una funzione di avere un alto grado di generalità, gestendo qualsiasi tipo di dato che probabilmente verrà a suo modo, ovvero "digitazione anatra". Nota il trattino basso principale su _ver_file ; questo è un modo convenzionale di designare una funzione "privata" (o metodo). Anche se tecnicamente può essere chiamato direttamente, suggerisce che la funzione non è destinata al consumo esterno diretto.

    
risposta data 10.11.2014 - 21:04
fonte
4

Se si passa il nome del file al posto dell'handle del file, non si garantisce che il secondo file sia lo stesso del primo quando viene aperto; questo può portare a errori di correttezza e buchi di sicurezza.

    
risposta data 11.11.2014 - 01:47
fonte

Leggi altre domande sui tag