Qual è un'alternativa a execfile in Python 3?


352

Sembra che abbiano cancellato in Python 3 tutto il modo semplice per caricare rapidamente uno script rimuovendolo execfile()

C'è un'alternativa ovvia che mi manca?


1
reloadè tornato, come imp.reload, dal 3.2.
Dougal,

18
Se stai usando Python in modo interattivo, considera l'utilizzo di IPython: %run script_namefunziona con tutte le versioni di Python.
Michael,

1
Poiché 3.4 impè importlib (che deve essere importato): importlib.reload(mod_name)importa ed esegue mod_name.
P. Wormer,

3
cosa c'è che non va nel runfile ("nomefile.py")?
Mousomer,

1
Grazie @mousomer !! Stavo proprio cercando la funzionalità di da runfile()quando avevo bisogno di eseguire uno script Python che viene eseguito nel suo spazio dei nomi (invece di eseguire sullo spazio dei nomi chiamante ). La mia applicazione: aggiungi la directory dello script chiamato al percorso di sistema ( sys.path) usando l' __file__attributo: se usiamo execfile()o il suo equivalente in Python 3 ( exec(open('file.py').read())) lo script incluso viene eseguito nello spazio dei nomi chiamante e quindi si __file__risolve nel nome del file chiamante .
Mastropi,

Risposte:


389

Secondo la documentazione , invece di

execfile("./filename") 

Uso

exec(open("./filename").read())

Vedere:


54
Qualche idea sul perché avrebbero fatto una cosa del genere? Questo è molto più dettagliato di prima. Inoltre, non funziona per me su Python3.3. Ottengo "Nessun file o directory" quando eseguo (apri ('./ some_file'). Read ()). Ho provato a includere l'estensione ".py" e anche a escludere "./"
JoeyC

25
Meno banalmente, questo non fornisce numeri di riga quando vengono sollevate eccezioni, come pure execfile ().
KDN

35
Dovrai closeanche gestire quel file. Un altro motivo per non gradire il passaggio da Python 2.
Rebs

3
@Rebs non è necessario chiudere l'handle del file in questo esempio, verrà eseguito automaticamente (almeno in CPython normale)
tiho,

4
Gli oggetti @Rebs negli oggetti CPython vengono raccolti in modo errato non appena il loro conteggio dei riferimenti arriva a 0, solo i riferimenti circolari possono ritardarlo ( stackoverflow.com/questions/9449489/… ). In tal caso ciò dovrebbe accadere subito dopo la restituzione di read (). E gli oggetti file vengono chiusi al momento dell'eliminazione (NB: mi rendo conto che questo link dice esplicitamente "chiudi sempre i file", che in effetti è una buona pratica da seguire in generale)
tiho,

219

Devi solo leggere il file ed eseguire il codice da solo. 2to3 sostituisce corrente

execfile("somefile.py", global_vars, local_vars)

come

with open("somefile.py") as f:
    code = compile(f.read(), "somefile.py", 'exec')
    exec(code, global_vars, local_vars)

(La chiamata di compilazione non è strettamente necessaria, ma associa il nome file all'oggetto codice rendendo il debug un po 'più semplice.)

Vedere:


3
Questo funziona per me. Tuttavia, ho notato che hai scritto gli argomenti locali e globali nell'ordine sbagliato. In realtà è: exec (object [, globals [, locals]]). Naturalmente se hai avuto gli argomenti capovolti nell'originale, 2to3 produrrà esattamente quello che hai detto. :)
Nathan Shively-Sanders,

3
Sono stato contento di scoprire che, se riesci a omettere global_vars e local_vars, qui la sostituzione di python3 funziona anche con python2. Anche se execè un'affermazione in python2, exec(code)funziona perché i genitori vengono semplicemente ignorati.
medmunds

2
+1 per l'utilizzo della compilazione. Il mio "somefile.py"contenuto inspect.getsourcefile(lambda _: None)non funzionava senza la compilazione, perché il inspectmodulo non era in grado di determinare da dove provenisse il codice.
ArtOfWarfare,

16
È davvero brutto. Hai idea del perché si sono sbarazzati di execfile () in 3.x? execfile ha anche facilitato il passaggio degli argomenti della riga di comando.
Aneddodeal

3
open("somefile.py")potrebbe essere errato se somefile.pyutilizza una codifica di caratteri diversa da locale.getpreferredencoding(). tokenize.open()potrebbe essere usato invece.
jfs

73

Mentre exec(open("filename").read())viene spesso fornito in alternativa a execfile("filename"), manca dettagli importanti che execfilesupportano.

La seguente funzione per Python3.x è la più vicina possibile allo stesso comportamento dell'esecuzione diretta di un file. Questo corrisponde alla corsa python /path/to/somefile.py.

def execfile(filepath, globals=None, locals=None):
    if globals is None:
        globals = {}
    globals.update({
        "__file__": filepath,
        "__name__": "__main__",
    })
    with open(filepath, 'rb') as file:
        exec(compile(file.read(), filepath, 'exec'), globals, locals)

# execute the file
execfile("/path/to/somefile.py")

Appunti:

  • Utilizza la lettura binaria per evitare problemi di codifica
  • Garantito per chiudere il file (Python3.x avverte di questo)
  • Definisce __main__, alcuni script dipendono da questo per verificare se si stanno caricando come modulo o no per es.if __name__ == "__main__"
  • L'impostazione __file__è migliore per i messaggi di eccezione e alcuni script utilizzano __file__per ottenere i percorsi di altri file relativi ad essi.
  • Accetta argomenti globali e locali opzionali, modificandoli sul posto come execfilefa, in modo da poter accedere a qualsiasi variabile definita leggendo le variabili dopo l'esecuzione.

  • A differenza di Python2, execfilequesto non modifica lo spazio dei nomi corrente per impostazione predefinita. Per questo devi esplicitamente passare globals()& locals().


68

Come suggerito recentemente sulla mailing list di python-dev , il modulo runpy potrebbe essere una valida alternativa. Citando da quel messaggio:

https://docs.python.org/3/library/runpy.html#runpy.run_path

import runpy
file_globals = runpy.run_path("file.py")

Ci sono sottili differenze rispetto a execfile:

  • run_pathcrea sempre un nuovo spazio dei nomi. Esegue il codice come modulo, quindi non c'è differenza tra globali e locali (motivo per cui esiste solo un init_globalsargomento). I globi vengono restituiti.

    execfileeseguito nello spazio dei nomi corrente o nello spazio dei nomi indicato. La semantica di localse globals, se data, erano simili a locali e globali all'interno di una definizione di classe.

  • run_path può non solo eseguire file, ma anche uova e directory (fare riferimento alla sua documentazione per i dettagli).


1
Per qualche motivo, invia sullo schermo molte informazioni che non è stato chiesto di stampare (" builtin " ecc. In Anaconda Python 3). C'è un modo per disattivarlo in modo che vengano visualizzate solo le informazioni che ho generato con print ()?
John Donn,

È anche possibile ottenere tutte le variabili nell'area di lavoro corrente anziché memorizzarle tutte file_globals? Ciò eviterebbe di dover digitare il file_globals['...']per ogni variabile.
Adriaan,

1
"Inoltre, tutte le funzioni e le classi definite dal codice eseguito non sono garantite per funzionare correttamente dopo il ritorno di una funzione runpy." Vale la pena notare, a seconda del caso d'uso
nodakai,

@Adriaan Execute "globals (). Update (file_globals)". Personalmente preferisco questa soluzione perché posso eventualmente rilevare errori prima di decidere di aggiornare l'area di lavoro corrente.
Ron Kaminsky,

@nodakai Grazie per l'informazione, mi sono perso. Non ho mai avuto problemi del genere, mi chiedo che cosa possa scatenarlo.
Ron Kaminsky,

21

Questo è meglio, dal momento che prende i globali e i locali dal chiamante:

import sys
def execfile(filename, globals=None, locals=None):
    if globals is None:
        globals = sys._getframe(1).f_globals
    if locals is None:
        locals = sys._getframe(1).f_locals
    with open(filename, "r") as fh:
        exec(fh.read()+"\n", globals, locals)

In realtà, questo è il più vicino a py2 execfile. Ha funzionato anche per me quando ho usato i pytest dove altre soluzioni pubblicate sopra non sono riuscite. Grazie! :)
Boriel,

17

Puoi scrivere la tua funzione:

def xfile(afile, globalz=None, localz=None):
    with open(afile, "r") as fh:
        exec(fh.read(), globalz, localz)

Se hai davvero bisogno di ...


1
-1: la dichiarazione exec non funziona in questo modo. Il codice non viene eseguito in nessuna versione di Python.
nosklo,

6
-1: I valori dei parametri predefiniti vengono valutati al momento della definizione della funzione, facendo entrambi globalse localspuntano allo spazio dei nomi globale del modulo contenente la definizione execfile()piuttosto che allo spazio dei nomi globale e locale del chiamante. L'approccio corretto consiste nell'utilizzare Nonecome valore predefinito e determinare i globi e i locali del chiamante tramite le capacità di introspezione del inspectmodulo.
Sven Marnach,

12

Se lo script che vuoi caricare si trova nella stessa directory di quella che esegui, forse "import" farà il lavoro?

Se devi importare dinamicamente il codice, vale la pena guardare la funzione integrata __ import__ e il modulo imp .

>>> import sys
>>> sys.path = ['/path/to/script'] + sys.path
>>> __import__('test')
<module 'test' from '/path/to/script/test.pyc'>
>>> __import__('test').run()
'Hello world!'

test.py:

def run():
        return "Hello world!"

Se stai usando Python 3.1 o versioni successive, dovresti anche dare un'occhiata a importlib .


Questa è stata la risposta corretta per me. Questo blog fa un buon lavoro spiegando importlib dev.to/0xcrypto/dynamic-importing-stuff-in-python--1805
Nick Brady

9

Ecco cosa avevo ( fileè già assegnato al percorso del file con il codice sorgente in entrambi gli esempi):

execfile(file)

Ecco cosa l'ho sostituito con:

exec(compile(open(file).read(), file, 'exec'))

La mia parte preferita: la seconda versione funziona perfettamente in Python 2 e 3, il che significa che non è necessario aggiungere una logica dipendente dalla versione.


5

Si noti che il modello precedente non funzionerà se si utilizzano dichiarazioni di codifica PEP-263 che non sono ASCII o UTF-8. Devi trovare la codifica dei dati e codificarla correttamente prima di consegnarla a exec ().

class python3Execfile(object):
    def _get_file_encoding(self, filename):
        with open(filename, 'rb') as fp:
            try:
                return tokenize.detect_encoding(fp.readline)[0]
            except SyntaxError:
                return "utf-8"

    def my_execfile(filename):
        globals['__file__'] = filename
        with open(filename, 'r', encoding=self._get_file_encoding(filename)) as fp:
            contents = fp.read()
        if not contents.endswith("\n"):
            # http://bugs.python.org/issue10204
            contents += "\n"
        exec(contents, globals, globals)

3
Che cos'è "il modello sopra"? Utilizzare i collegamenti quando si fa riferimento ad altri post su StackOverflow. I termini di posizionamento relativo come "quanto sopra" non funzionano, in quanto vi sono 3 modi diversi di ordinare le risposte (per voti, per data o per attività) e il più comune (per voti) è volatile. Nel tempo il tuo post e i post attorno ai tuoi finiranno con punteggi diversi, il che significa che saranno riorganizzati e tali confronti saranno meno utili.
ArtOfWarfare,

Ottimo punto E dato che ho scritto questa risposta quasi sei mesi fa, presumo per "sopra lo schema" intendevo stackoverflow.com/a/2849077/165082 (che sfortunatamente devi cliccare su per risolvere), o meglio ancora la risposta di Noam:
Eric

2
In genere, quando desidero fare riferimento ad altre risposte alla stessa domanda dalla mia risposta, digito "Noam's Answer" (ad esempio) e collego il testo alla risposta a cui mi riferisco, solo nel caso in cui la risposta venga dissociata dal utente in futuro, IE, perché l'utente cambia il proprio nome account o il post diventa un wiki comune perché sono state apportate troppe modifiche su di esso.
ArtOfWarfare il

Come si ottiene l'URL di una "risposta" specifica con in un post, escludendo il nome del poster della risposta?
DevPlayer

Visualizza la fonte e ottieni l'ID. Ad esempio, la tua domanda sarebbe stackoverflow.com/questions/436198/… . Sto cercando un metodo migliore, ma non vedo nulla quando mi alzo vicino al commento
Eric

4

Inoltre, pur non essendo una soluzione Python pura, se stai usando IPython (come probabilmente dovresti comunque), puoi fare:

%run /path/to/filename.py

È altrettanto facile.


1

Sono solo un principiante qui, quindi forse è pura fortuna se ho trovato questo:

Dopo aver provato a eseguire uno script dal prompt dell'interprete >>> con il comando

    execfile('filename.py')

per cui ho ottenuto un "NameError: il nome 'execfile' non è definito" Ho provato un metodo molto semplice

    import filename

ha funzionato bene :-)

Spero che questo possa essere utile e grazie a tutti per i grandi suggerimenti, esempi e tutti quei pezzi di codice commentati magistralmente che sono di grande ispirazione per i nuovi arrivati!

Uso Ubuntu 16.014 LTS x64. Python 3.5.2 (impostazione predefinita, 17 novembre 2016, 17:05:23) [GCC 5.4.0 20160609] su Linux


0

Per me, l'approccio più pulito è quello di utilizzare importlibe importare il file come modulo per percorso, in questo modo:

from importlib import util

def load_file(name, path):
    spec = util.spec_from_file_location(name, path)
    module = util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

Esempio di utilizzo

Diamo un file foo.py:

print('i got imported')
def hello():
    print('hello from foo')

Ora importalo e usalo come un normale modulo:

>>> foo = load_file('foo', './foo.py')
i got imported
>>> foo.hello()
hello from foo

Preferisco questa tecnica rispetto ad approcci diretti come exec(open(...))perché non ingombrano i tuoi spazi dei nomi o rovinano inutilmente $PATH.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.