it-swarm.com.de

Ausführung von Python-Code mit der Option -m oder nicht

Der Python-Interpreter hat die -m module -Option, die "das Bibliotheksmodul module als Skript ausführt". 

Mit diesem Python-Code a.py:

if __== "__main__":
    print __package__
    print __name__

Ich habe python -m a getestet um zu bekommen

"" <-- Empty String
__main__

wohingegen python a.py zurückkehrt

None <-- None
__main__

Für mich scheinen diese beiden Aufrufe identisch zu sein, außer dass __package__ keine ist, wenn sie mit der Option -m aufgerufen wird. 

Interessanterweise bekomme ich mit python -m runpy a dasselbe wie python -m a mit einem Python-Modul, das kompiliert wurde, um a.pyc zu erhalten. 

Was ist der (praktische) Unterschied zwischen diesen Anrufungen? Irgendwelche Vor- und Nachteile zwischen ihnen? 

In David Beazleys Python Essential Reference wird es auch so erklärt: "Die Option -m führt ein Bibliotheksmodul als Skript aus, das vor der Ausführung des Hauptskripts im Modul __main__ ausgeführt wird". Was heißt das?

56
prosseek

Wenn Sie das Befehlszeilenflag -m verwenden, importiert Python ein Modul oder Paket für dich, dann starte es als script. Wenn Sie das Flag _-m_ nicht verwenden, wird die von Ihnen angegebene Datei als nur als Skript ausgeführt.

Die Unterscheidung ist wichtig, wenn Sie versuchen, ein Paket auszuführen. Es gibt einen großen Unterschied zwischen:

_python foo/bar/baz.py
_

und

_python -m foo.bar.baz
_

wie im letzteren Fall wird _foo.bar_ importiert und relative Importe funktionieren korrekt mit _foo.bar_ als Ausgangspunkt.

Demo:

_$ mkdir -p test/foo/bar
$ touch test/foo/__init__.py
$ touch test/foo/bar/__init__.py
$ cat << EOF > test/foo/bar/baz.py 
> if __== "__main__":
>     print __package__
>     print __name__
> 
> EOF
$ PYTHONPATH=test python test/foo/bar/baz.py 
None
__main__
$ PYTHONPATH=test bin/python -m foo.bar.baz 
foo.bar
__main__
_

Daher muss sich Python tatsächlich um Pakete kümmern, wenn der Schalter _-m_ verwendet wird. Ein normales Skript kann niemals ein Paket sein , daher wird ___package___ auf None gesetzt.

Führen Sie jedoch ein Paket oder Modul in einem Paket mit _-m_ aus, und jetzt besteht zumindest die Möglichkeit eines Pakets, daher wird die Variable ___package___ auf einen Zeichenfolgenwert gesetzt. In der obigen Demonstration ist es auf _foo.bar_ gesetzt. Für einfache Module, die sich nicht in einem Paket befinden, ist es auf eine leere Zeichenfolge gesetzt.

Wie für das Modul ___main___ ; Python importiert Skripte, die wie ein reguläres Modul ausgeführt werden. Ein neues Modulobjekt wird erstellt, das den globalen Namespace enthält, der in _sys.modules['__main__']_ gespeichert ist. Dies ist, worauf sich die Variable ___name___ bezieht, sie ist ein Schlüssel in dieser Struktur.

Für Pakete können Sie ein Modul ___main__.py_ erstellen und dieses beim Ausführen von _python -m package_name_ ausführen lassen. Nur so können Sie ein Paket als Skript ausführen :

_$ PYTHONPATH=test python -m foo.bar
python: No module named foo.bar.__main__; 'foo.bar' is a package and cannot be directly executed
$ cp test/foo/bar/baz.py test/foo/bar/__main__.py
$ PYTHONPATH=test python -m foo.bar
foo.bar
__main__
_

Wenn Sie also ein Paket für die Ausführung mit _-m_ benennen, sucht Python nach einem in diesem Paket enthaltenen ___main___ -Modul und führt dieses als Skript aus. Der Name lautet dann weiterhin ___main___, und das Modulobjekt ist weiterhin in _sys.modules['__main__']_ gespeichert.

115
Martijn Pieters

Ausführung von Python-Code mit der Option -m oder nicht

Verwenden Sie das Flag -m

Die Ergebnisse sind bei Verwendung eines Skripts fast gleich. Wenn Sie jedoch ein Paket ohne das Kennzeichen -m entwickeln, können Sie die Importe nicht ordnungsgemäß ausführen, wenn Sie ein Unterpaket oder ein Modul im Paket als main ausführen möchten Einstiegspunkt zu Ihrem Programm (und glauben Sie mir, ich habe es versucht.)

Die docs

Wie die Dokumente sagen:

Durchsuchen Sie sys.path nach dem benannten Modul und führen Sie dessen Inhalt als __main__-Modul aus.

und

Wie bei der Option -c wird das aktuelle Verzeichnis am Anfang von sys.path hinzugefügt.

so

python -m pdb

entspricht ungefähr

python /usr/lib/python3.5/pdb.py

(vorausgesetzt, Sie haben in Ihrem aktuellen Verzeichnis kein Paket oder Skript namens pdb.py)

Erläuterung:

Das Verhalten wird "absichtlich ähnlich" gemacht.

Viele Standardbibliotheksmodule enthalten Code, der bei der Ausführung als Skript aufgerufen wird. Ein Beispiel ist das Modul timeit:

Bestimmter Python-Code soll als Modul ausgeführt werden: (Ich denke, dieses Beispiel ist besser als das Befehlszeilenoptionsdok-Beispiel).

$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop
$ python -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 3: 33.4 usec per loop
$ python -m timeit '"-".join(map(str, range(100)))'
10000 loops, best of 3: 25.2 usec per loop

Und aus den Versionshinweisen für Python 2.4 :

Die Befehlszeilenoption -m - python -m Modulname findet ein Modul in der Standardbibliothek, und rufen Sie es auf. Zum Beispiel python -m pdb ist äquivalent zu python /usr/lib/python2.4/pdb.py

Zusatzfrage

In David Beazleys Python Essential Reference wird es auch als "Die Option -M führt ein Bibliotheksmodul als Skript aus, das vor der Ausführung des Hauptskripts im Modul __main__ ausgeführt wird".

Das bedeutet, dass jedes Modul, das Sie mit einer Importanweisung nachschlagen können, als Einstiegspunkt des Programms ausgeführt werden kann - wenn es einen Codeblock mit if __== '__main__': (normalerweise nahe dem Ende) hat.

-m, ohne das aktuelle Verzeichnis dem Pfad hinzuzufügen:

Ein Kommentar hier an anderer Stelle sagt:

Dass die Option -m auch das aktuelle Verzeichnis zu sys.path hinzufügt, ist offensichtlich ein Sicherheitsproblem (siehe: Angriff vor Laden). Dieses Verhalten ähnelt der Bibliotheksuchreihenfolge in Windows (bevor es kürzlich gehärtet wurde). Schade, dass Python dem Trend nicht folgt und keine einfache Möglichkeit bietet, das Hinzufügen zu deaktivieren. zu sys.path

Nun, das demonstriert das mögliche Problem - (in Windows die Anführungszeichen entfernen):

echo "import sys; print(sys.version)" > pdb.py

python -m pdb
3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul  5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)]

Verwenden Sie das Flag -I, um dies für Produktionsumgebungen zu sperren (neu in Version 3.4):

python -Im pdb
usage: pdb.py [-c command] ... pyfile [arg] ...
etc...

von die Dokumente :

-I

Führen Sie Python im isolierten Modus aus. Dies impliziert auch -E und -s. Im isolierten Modus enthält sys.path weder das Verzeichnis des Skripts noch das Verzeichnis der Site-Packages des Benutzers. Alle Umgebungsvariablen von PYTHON * werden ebenfalls ignoriert. Es können weitere Einschränkungen auferlegt werden, um zu verhindern, dass der Benutzer schädlichen Code injiziert.

Was macht __package__?

Es ermöglicht explizite relative Importe, die jedoch für diese Frage nicht besonders relevant sind. Diese Antwort finden Sie hier: Wozu dient das Attribut "__package__" in Python?

13
Aaron Hall

Der Hauptgrund, ein Modul (oder ein Paket) als Skript mit -m auszuführen, ist die Vereinfachung der Bereitstellung, insbesondere unter Windows. Sie können Skripts an derselben Stelle in der Python-Bibliothek installieren, an der sich normalerweise Module befinden - anstatt PATH oder globale ausführbare Verzeichnisse wie ~/.local zu verschmutzen (das Skriptsverzeichnis für Benutzer ist unter Windows nur schwer zu finden). 

Dann tippen Sie einfach -m und Python findet das Skript automatisch. Beispielsweise findet python -m pip den richtigen Pip für dieselbe Instanz des Python-Interpreters, der ihn ausführt. Ohne -m, wenn der Benutzer mehrere Python-Versionen installiert hat, welche wäre der "globale" Pip?

Wenn Benutzer "klassische" Einstiegspunkte für Befehlszeilenskripts vorziehen, können diese leicht als kleine Skripts irgendwo in PATH hinzugefügt werden, oder pip kann diese zur Installationszeit mit dem Parameter entry_points in setup.py erstellen. 

Also einfach nach __== '__main__' suchen und andere nicht zuverlässige Implementierungsdetails ignorieren. 

0
ddbug