Was ist die __main__.py
Datei zu erstellen, welche Art von Code sollte ich darin eingeben und wann sollte ich eine Datei erstellen?
Antworten
Zu viele Anzeigen?Oft wird ein Python-Programm ausgeführt, indem man eine .py-Datei auf der Befehlszeile angibt:
$ python my_program.py
Sie können auch ein Verzeichnis oder eine Zip-Datei mit Code erstellen und eine __main__.py
. Dann können Sie einfach den Namen des Verzeichnisses oder der Zip-Datei in der Befehlszeile eingeben, und das Programm führt die __main__.py
automatisch:
$ python my_program_dir
$ python my_program.zip
# Or, if the program is accessible as a module
$ python -m my_program
Sie müssen selbst entscheiden, ob Ihre Anwendung von einer solchen Ausführung profitieren könnte.
Beachten Sie, dass eine __main__
Modul kommt normalerweise nicht von einer __main__.py
Datei. Das ist möglich, aber normalerweise nicht der Fall. Wenn Sie ein Skript wie python my_program.py
wird das Skript als __main__
Modul anstelle des my_program
Modul. Dies geschieht auch bei Modulen, die als python -m my_module
oder auf verschiedene andere Weise.
Wenn Sie den Namen __main__
in einer Fehlermeldung auftaucht, bedeutet das nicht unbedingt, dass Sie nach einem Fehler suchen sollten. __main__.py
archivo.
Was ist die __main__.py
Datei für?
Bei der Erstellung eines Python-Moduls ist es üblich, dass das Modul eine bestimmte Funktionalität ausführt (die normalerweise in einer main
Funktion), wenn sie als Einstiegspunkt des Programms ausgeführt wird. Dies wird typischerweise mit dem folgenden gemeinsamen Idiom am Ende der meisten Python-Dateien gemacht:
if __name__ == '__main__':
# execute only if run as the entry point into the program
main()
Sie können die gleiche Semantik für ein Python-Paket mit __main__.py
die die folgende Struktur haben könnte:
.
demo
__init__.py
__main__.py
Um dies zu sehen, fügen Sie den folgenden Text in eine Python 3-Shell ein:
from pathlib import Path
demo = Path.cwd() / 'demo'
demo.mkdir()
(demo / '__init__.py').write_text("""
print('demo/__init__.py executed')
def main():
print('main() executed')
""")
(demo / '__main__.py').write_text("""
print('demo/__main__.py executed')
from demo import main
main()
""")
Wir können Demo als ein Paket behandeln und es tatsächlich importieren, was den Top-Level-Code in der __init__.py
(aber nicht die main
Funktion):
>>> import demo
demo/__init__.py executed
Wenn wir das Paket als Einstiegspunkt für das Programm verwenden, führen wir den Code in der __main__.py
die die __init__.py
Erstens:
$ python -m demo
demo/__init__.py executed
demo/__main__.py executed
main() executed
Sie können dies aus der Dokumentation entnehmen. Die Dokumentation dit :
__main__
- Skriptumgebung der obersten Ebene
'__main__'
ist der Name des Bereichs, in dem der Code der obersten Ebene ausgeführt wird. Der Bereich eines Moduls__name__
gleichgesetzt wird mit'__main__'
beim Lesen von Standard Eingabe, einem Skript oder von einer interaktiven Eingabeaufforderung gelesen wird.Ein Modul kann feststellen, ob es im Hauptbereich läuft oder nicht läuft, indem es seine eigene
__name__
die ein gemeinsames Idiom ermöglicht für bedingte Ausführung von Code in einem Modul, wenn es als Skript oder mitpython -m
aber nicht, wenn es importiert wird:if __name__ == '__main__': # execute only if run as a script main()
Bei einem Paket kann derselbe Effekt erzielt werden, indem eine
__main__.py
Modul, dessen Inhalt ausgeführt wird, wenn das Modul mit-m
.
Mit Reißverschluss
Sie können dieses Verzeichnis auch komprimieren, einschließlich der __main__.py
in eine einzige Datei packen und von der Kommandozeile aus wie folgt ausführen - beachten Sie jedoch, dass gepackte Pakete keine Unterpakete oder Untermodule als Einstiegspunkt ausführen können:
from pathlib import Path
demo = Path.cwd() / 'demo2'
demo.mkdir()
(demo / '__init__.py').write_text("""
print('demo2/__init__.py executed')
def main():
print('main() executed')
""")
(demo / '__main__.py').write_text("""
print('demo2/__main__.py executed')
from __init__ import main
main()
""")
Beachten Sie die subtile Änderung - wir importieren main
de __init__
代わりに demo2
- dieses gezippte Verzeichnis wird nicht als Paket, sondern als Verzeichnis mit Skripten behandelt. Daher muss es ohne das -m
Flagge.
Besonders relevant für die Frage - zipapp
veranlasst das gezippte Verzeichnis, die __main__.py
standardmäßig - und sie wird zuerst ausgeführt, bevor __init__.py
:
$ python -m zipapp demo2 -o demo2zip
$ python demo2zip
demo2/__main__.py executed
demo2/__init__.py executed
main() executed
Nochmals: Dieses gezippte Verzeichnis ist kein Paket - Sie können es auch nicht importieren.
Einige der Antworten hier implizieren, dass ein "Paket"-Verzeichnis (mit oder ohne explizite __init__.py
Datei), die eine __main__.py
Datei, gibt es keinen Unterschied zwischen der Ausführung dieses Verzeichnisses mit dem -m
Schalter oder ohne.
Der große Unterschied besteht darin, dass ohne el -m
Schalter, das Verzeichnis "package" wird zunächst dem Pfad hinzugefügt (d.h. sys.path), und dann werden die Dateien normal ausgeführt, ohne Paketsemantik .
. mit el -m
Schalter, die Semantik der Pakete (einschließlich relativer Importe) wird beachtet und das Paketverzeichnis selbst wird nie zum Systempfad hinzugefügt .
Dies ist eine sehr wichtige Unterscheidung, sowohl in Bezug auf die Frage, ob relative Importe funktionieren oder nicht, als auch, was noch wichtiger ist, in Bezug auf das Diktat der was im Falle einer unbeabsichtigten Verschattung von Systemmodulen importiert wird .
Beispiel:
Nehmen wir ein Verzeichnis namens PkgTest
mit der folgenden Struktur
:~/PkgTest$ tree
.
pkgname
__main__.py
secondtest.py
testmodule.py
testmodule.py
wo die __main__.py
hat den folgenden Inhalt:
:~/PkgTest$ cat pkgname/__main__.py
import os
print( "Hello from pkgname.__main__.py. I am the file", os.path.abspath( __file__ ) )
print( "I am being accessed from", os.path.abspath( os.curdir ) )
from testmodule import main as firstmain; firstmain()
from .secondtest import main as secondmain; secondmain()
(wobei die anderen Dateien in ähnlicher Weise definiert sind und ähnliche Ausdrucke aufweisen).
Wenn Sie dies ohne die -m
wechseln, erhalten Sie Folgendes. Beachten Sie, dass der relative Import fehlschlägt, aber was noch wichtiger ist, beachten Sie, dass das falsche Testmodul ausgewählt wurde (d.h. relativ zum Arbeitsverzeichnis):
:~/PkgTest$ python3 pkgname
Hello from pkgname.__main__.py. I am the file ~/PkgTest/pkgname/__main__.py
I am being accessed from ~/PkgTest
Hello from testmodule.py. I am the file ~/PkgTest/pkgname/testmodule.py
I am being accessed from ~/PkgTest
Traceback (most recent call last):
File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "pkgname/__main__.py", line 10, in <module>
from .secondtest import main as secondmain
ImportError: attempted relative import with no known parent package
Mit der Option -m hingegen erhalten Sie das, was Sie (hoffentlich) erwartet haben:
:~/PkgTest$ python3 -m pkgname
Hello from pkgname.__main__.py. I am the file ~/PkgTest/pkgname/__main__.py
I am being accessed from ~/PkgTest
Hello from testmodule.py. I am the file ~/PkgTest/testmodule.py
I am being accessed from ~/PkgTest
Hello from secondtest.py. I am the file ~/PkgTest/pkgname/secondtest.py
I am being accessed from ~/PkgTest
Anmerkung: Meiner ehrlichen Meinung nach ist das Laufen ohne -m
sollten vermieden werden. Ich würde sogar noch weiter gehen und sagen, dass ich jede executable packages
so, dass sie fehlschlagen würden, wenn sie nicht über den -m
Schalter.
Mit anderen Worten, ich würde nur von "In-Package"-Modulen explizit über "relative Importe" importieren, in der Annahme, dass alle anderen Importe Systemmodule darstellen. Wenn jemand versucht, Ihr Paket ohne die -m
werden die relativen Import-Anweisungen einen Fehler auslösen, anstatt das falsche Modul stillschweigend auszuführen.
__main__.py
wird für Python-Programme in Zip-Dateien verwendet. Die __main__.py
Datei wird ausgeführt, wenn die Zip-Datei ausgeführt wird. Zum Beispiel, wenn die Zip-Datei war als solche:
test.zip
__main__.py
und der Inhalt von __main__.py
war
import sys
print "hello %s" % sys.argv[1]
Wenn wir dann die python test.zip world
würden wir hello world
aus.
Also die __main__.py
Datei ausgeführt, wenn Python für eine Zip-Datei aufgerufen wird.
- See previous answers
- Weitere Antworten anzeigen