Was ist eine Alternative zu Execfile in Python 3?

352

Es scheint, dass sie in Python 3 auf einfache Weise abgebrochen wurden, um ein Skript durch Entfernen schnell zu laden execfile()

Gibt es eine offensichtliche Alternative, die mir fehlt?

RS
quelle
1
reloadist zurück, da imp.reloadseit 3.2.
Dougal
18
Wenn Sie Python interaktiv verwenden, sollten Sie IPython verwenden: Funktioniert %run script_namemit allen Python-Versionen.
Michael
1
Da 3.4 impist importlib (was importiert werden muss): importlib.reload(mod_name)importiert und führt aus mod_name.
P. Wormer
3
Was ist los mit runfile ("filename.py")?
Mausomer
1
Danke @mousomer !! Ich habe genau nach der Funktionalität gesucht, runfile()da ich ein Python-Skript ausführen musste, das in einem eigenen Namespace ausgeführt wird (im Gegensatz zur Ausführung im aufrufenden Namespace). Meine Anwendung: Fügen Sie das Verzeichnis des aufgerufenen Skripts sys.pathmit dem __file__Attribut zum Systempfad ( ) hinzu : Wenn wir execfile()Python 3 ( exec(open('file.py').read())) oder dessen Äquivalent verwenden, wird das enthaltene Skript im aufrufenden Namespace ausgeführt und daher __file__in den aufrufenden Dateinamen aufgelöst.
Mastropi

Antworten:

389

Laut Dokumentation statt

execfile("./filename") 

Verwenden

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

Sehen:

Pedro Vagner
quelle
54
Irgendeine Idee, warum sie so etwas tun würden? Das ist so viel ausführlicher als zuvor. Außerdem funktioniert es bei Python3.3 nicht. Ich erhalte "Keine solche Datei oder kein solches Verzeichnis", wenn ich (open ('./ some_file'). Read ()) ausführe. Ich habe versucht, die Erweiterung '.py'
einzuschließen
25
Weniger trivial ist, dass dies keine Zeilennummern liefert, wenn Ausnahmen ausgelöst werden, ebenso wie execfile ().
KDN
35
Sie müssen auch closediese Datei behandeln. Ein weiterer Grund, die Änderung von Python 2 nicht zu mögen.
Rebs
3
@Rebs Sie müssen das Dateihandle in diesem Beispiel nicht schließen, es wird automatisch durchgeführt (zumindest in
normalem
4
@Rebs in CPython-Objekten werden mit Müll gesammelt, sobald ihre Referenzanzahl auf 0 geht. Nur Zirkelverweise können dies verzögern ( stackoverflow.com/questions/9449489/… ). In diesem Fall sollte dies direkt nach der Rückkehr von read () geschehen. Und Dateiobjekte werden beim Löschen geschlossen (Hinweis: Mir ist klar, dass dieser Link explizit "Dateien immer schließen" sagt, was in der Tat eine gute Vorgehensweise im Allgemeinen ist)
2.
219

Sie müssen nur die Datei lesen und den Code selbst ausführen. 2to3 Strom ersetzt

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

wie

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

(Der Kompilierungsaufruf wird nicht unbedingt benötigt, ordnet jedoch den Dateinamen dem Codeobjekt zu, was das Debuggen etwas erleichtert.)

Sehen:

Benjamin Peterson
quelle
3
Das funktioniert bei mir. Mir ist jedoch aufgefallen, dass Sie die lokalen und globalen Argumente in der falschen Reihenfolge geschrieben haben. Es ist eigentlich: exec (Objekt [, Globale [, Einheimische]]). Wenn Sie die Argumente im Original umgedreht haben, wird 2to3 natürlich genau das produzieren, was Sie gesagt haben. :)
Nathan Shively-Sanders
3
War erfreut zu entdecken, dass, wenn Sie global_vars und local_vars weglassen können, der Python3-Ersatz hier auch unter Python2 funktioniert. Obwohl execes sich um eine Anweisung in python2 handelt, exec(code)funktioniert sie, weil die Parens einfach ignoriert werden.
Medmunds
2
+1 für die Verwendung von compile. Mein "somefile.py"Inhalt, inspect.getsourcefile(lambda _: None)der ohne die Kompilierung fehlschlug, weil das inspectModul nicht feststellen konnte, woher der Code kam.
ArtOfWarfare
16
Das ist ... wirklich hässlich. Irgendeine Idee, warum sie execfile () in 3.x losgeworden sind? Mit execfile war es auch einfach, Befehlszeilenargumente zu übergeben.
aneccodeal
3
open("somefile.py")kann falsch sein, wenn somefile.pyeine andere Zeichencodierung als verwendet wird locale.getpreferredencoding(). tokenize.open()könnte stattdessen verwendet werden.
JFS
73

Obwohl exec(open("filename").read())es oft als Alternative zu gegeben wird execfile("filename"), fehlen wichtige Details, die execfileunterstützt werden.

Die folgende Funktion für Python3.x ist so nah wie möglich am gleichen Verhalten wie das direkte Ausführen einer Datei. Das passt zum Laufen 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")

Anmerkungen:

  • Verwendet binäres Lesen, um Codierungsprobleme zu vermeiden
  • Garantiert das Schließen der Datei (Python3.x warnt davor)
  • Definiert __main__, einige Skripte hängen davon ab, um zu überprüfen, ob sie als Modul geladen werden oder nicht, z.if __name__ == "__main__"
  • Die Einstellung __file__ist für Ausnahmemeldungen __file__besser und einige Skripte verwenden , um die Pfade anderer Dateien relativ zu ihnen abzurufen.
  • Nimmt optionale globale und lokale Argumente auf und execfileändert sie an Ort und Stelle. So können Sie auf alle Variablen zugreifen, die durch Zurücklesen der Variablen nach dem Ausführen definiert wurden.

  • Im Gegensatz zu Python2 execfileändert dies standardmäßig nicht den aktuellen Namespace. Dafür müssen Sie globals()& explizit übergeben locals().

ideasman42
quelle
68

Wie kürzlich auf der Python-Dev- Mailingliste vorgeschlagen, ist das Runpy- Modul möglicherweise eine praktikable Alternative. Zitat aus dieser Nachricht:

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

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

Es gibt subtile Unterschiede zu execfile:

  • run_pathErstellt immer einen neuen Namespace. Es führt den Code als Modul aus, sodass es keinen Unterschied zwischen globalen und lokalen Elementen gibt (weshalb es nur ein init_globalsArgument gibt). Die Globals werden zurückgegeben.

    execfileim aktuellen Namespace oder im angegebenen Namespace ausgeführt. Die Semantik von localsund globals, falls angegeben, ähnelte den Einheimischen und Globalen innerhalb einer Klassendefinition.

  • run_path kann nicht nur Dateien, sondern auch Eier und Verzeichnisse ausführen (Einzelheiten finden Sie in der Dokumentation).

Jonas Schäfer
quelle
1
Aus irgendeinem Grund werden viele Informationen auf dem Bildschirm ausgegeben , die nicht gedruckt werden sollen (' integrierte ' usw. in Anaconda Python 3). Gibt es eine Möglichkeit, dies auszuschalten, damit nur die Informationen angezeigt werden, die ich mit print () ausgegeben habe?
John Donn
Ist es auch möglich, alle Variablen im aktuellen Arbeitsbereich abzurufen, anstatt sie alle zu speichern file_globals? Dies würde es ersparen, das file_globals['...']für jede Variable eingeben zu müssen.
Adriaan
1
"Darüber hinaus kann nicht garantiert werden, dass Funktionen und Klassen, die durch den ausgeführten Code definiert sind, ordnungsgemäß funktionieren, nachdem eine Runpy-Funktion zurückgegeben wurde." Bemerkenswert, abhängig von Ihrem Anwendungsfall
Nodakai
@Adriaan Führen Sie "globals (). Update (file_globals)" aus. Persönlich gefällt mir diese Lösung am besten, da ich möglicherweise Fehler abfangen kann, bevor ich mich entscheide, den aktuellen Arbeitsbereich zu aktualisieren.
Ron Kaminsky
@nodakai Danke für die Info, das habe ich verpasst. Ich hatte noch nie ein solches Problem, ich frage mich, was das wahrscheinlich auslösen wird.
Ron Kaminsky
21

Dieser ist besser, da er die Anrufer und Einheimischen vom Anrufer nimmt:

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)
Noam
quelle
Eigentlich ist dieser py2 näher execfile. Es funktionierte sogar für mich, wenn ich Pytests verwendete, bei denen andere oben angegebene Lösungen fehlschlugen. Danke! :)
Boriel
17

Sie könnten Ihre eigene Funktion schreiben:

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

Wenn Sie wirklich ...

Evan Fosmark
quelle
1
-1: Die Exec-Anweisung funktioniert nicht auf diese Weise. Code läuft in keiner Python-Version.
Nosklo
6
-1: Die Standard - Parameterwerte werden bei Funktionsdefinition ausgewertet, beide machen globalsund localszeigen Sie auf die globale Namespace fo das Modul die Definition enthält , execfile()anstatt auf den globalen und lokalen Namensraum des Anrufers. Der richtige Ansatz besteht darin, Noneals Standardwert die globalen und lokalen Werte des Anrufers über die Introspektionsfunktionen des inspectModuls zu bestimmen .
Sven Marnach
12

Wenn sich das Skript, das Sie laden möchten, im selben Verzeichnis befindet wie das, das Sie ausführen, erledigt "import" möglicherweise die Aufgabe?

Wenn Sie Code dynamisch importieren müssen, sollten Sie sich die integrierte Funktion __ import__ und das Modul imp ansehen .

>>> 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!"

Wenn Sie Python 3.1 oder höher verwenden, sollten Sie auch einen Blick darauf werfen importlib ansehen .

Ascobol
quelle
Das war die richtige Antwort für mich. Dieser Blog macht einen guten Job und erklärt importlib dev.to/0xcrypto/dynamic-importing-stuff-in-python--1805
Nick Brady
9

Folgendes hatte ich ( fileist in beiden Beispielen bereits dem Pfad zur Datei mit dem Quellcode zugeordnet):

execfile(file)

Folgendes habe ich ersetzt:

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

Mein Lieblingsteil: Die zweite Version funktioniert sowohl in Python 2 als auch in Python 3 einwandfrei, was bedeutet, dass es nicht erforderlich ist, eine versionabhängige Logik hinzuzufügen.

ArtOfWarfare
quelle
5

Beachten Sie, dass das obige Muster fehlschlägt, wenn Sie PEP-263-Codierungsdeklarationen verwenden, die nicht ASCII oder UtF-8 sind. Sie müssen die Codierung der Daten finden und korrekt codieren, bevor Sie sie an exec () übergeben.

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)
Eric
quelle
3
Was ist "das obige Muster"? Bitte verwenden Sie Links, wenn Sie auf andere Beiträge in StackOverflow verweisen. Relative Positionierungsbegriffe wie "die oben genannten" funktionieren nicht, da es drei verschiedene Möglichkeiten gibt, Antworten zu sortieren (nach Stimmen, Datum oder Aktivität) und die häufigste (nach Stimmen) volatil ist. Im Laufe der Zeit werden Ihre Posts und Posts um Ihre herum unterschiedliche Ergebnisse erzielen, was bedeutet, dass sie neu angeordnet werden und solche Vergleiche weniger nützlich sind.
ArtOfWarfare
Sehr guter Punkt. Und da ich diese Antwort vor fast sechs Monaten geschrieben habe, gehe ich davon aus, dass ich mit "obigem Muster" stackoverflow.com/a/2849077/165082 (auf das Sie leider klicken müssen, um es zu lösen) oder besser noch Noams Antwort gemeint habe :
Eric
2
Wenn ich aus meiner Antwort auf andere Antworten auf dieselbe Frage verweisen möchte, gebe ich im Allgemeinen "Noams Antwort" ein (zum Beispiel) und verknüpfe den Text mit der Antwort, auf die ich mich beziehe, nur für den Fall, dass die Antwort von der Antwort getrennt wird Benutzer in der Zukunft, IE, weil der Benutzer seinen Kontonamen ändert oder der Beitrag zu einem kommunalen Wiki wird, weil zu viele Änderungen daran vorgenommen wurden.
ArtOfWarfare
Wie erhält man die URL zu einer bestimmten "Antwort" in einem Beitrag, ohne den Namen des Posters der Antwort?
DevPlayer
Zeigen Sie die Quelle an und erhalten Sie die ID. Ihre Frage wäre beispielsweise stackoverflow.com/questions/436198/… . Ich bin alle für eine bessere Methode, aber sehe nichts, wenn ich in der Nähe des Kommentars schwebe
Eric
4

Auch wenn Sie keine reine Python-Lösung sind, können Sie mit IPython (wie Sie es wahrscheinlich sowieso tun sollten) Folgendes tun:

%run /path/to/filename.py

Welches ist ebenso einfach.

RS
quelle
1

Ich bin nur ein Neuling hier, also ist es vielleicht reines Glück, wenn ich Folgendes gefunden habe:

Nachdem Sie versucht haben, ein Skript an der Interpreter-Eingabeaufforderung >>> mit dem Befehl auszuführen

    execfile('filename.py')

für die ich einen "NameError: Name 'execfile' ist nicht definiert" bekam, habe ich einen sehr einfachen versucht

    import filename

es hat gut funktioniert :-)

Ich hoffe, dass dies hilfreich sein kann und danke Ihnen allen für die großartigen Hinweise, Beispiele und all die meisterhaft kommentierten Codeteile, die eine großartige Inspiration für Neulinge sind!

Ich benutze Ubuntu 16.014 LTS x64. Python 3.5.2 (Standard, 17. November 2016, 17:05:23) [GCC 5.4.0 20160609] unter Linux

Claude
quelle
0

Für mich ist der sauberste Ansatz, importlibdie Datei als Modul nach Pfad zu verwenden und zu importieren, wie folgt:

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

Anwendungsbeispiel

Lassen Sie uns eine Datei haben foo.py:

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

Jetzt einfach importieren und wie ein normales Modul verwenden:

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

Ich bevorzuge diese Technik gegenüber direkten Ansätzen, exec(open(...))weil sie Ihre Namespaces nicht überladen oder unnötig durcheinander bringt $PATH.

Arminius
quelle