__main__ — Περιβάλλον κώδικα ανωτέρου επιπέδου¶
Στην Python, το ειδικό όνομα __main__ χρησιμοποιείται για δύο σημαντικές κατασκευές:
το όνομα του πεδίου ονομάτων ανωτέρου επιπέδου του προγράμματος, το οποίο μπορεί να ελεγχθεί χρησιμοποιώντας την έκφραση
__name__ == '__main__'· καιτο αρχείο
__main__.pyστα πακέτα της Python.
Και οι δύο αυτοί μηχανισμοί σχετίζονται με τα modules της Python· τον τρόπο με τον οποίο οι χρήστες αλληλεπιδρούν με αυτά και τον τρόπο με τον οποίο αλληλεπιδρούν μεταξύ τους. Εξηγούνται λεπτομερώς παρακάτω. Εάν είστε νέος/α στα Python modules, δείτε την ενότητα του σεμιναρίου Modules για μια εισαγωγή.
__name__ == '__main__'¶
Όταν ένα Python module ή πακέτο εισάγεται, το __name__ ορίζεται στο όνομα του module. Συνήθως, αυτό είναι το όνομα του αρχείου Python χωρίς την επέκταση .py:
>>> import configparser
>>> configparser.__name__
'configparser'
Εάν το αρχείο είναι μέρος ενός πακέτου, το __name__ θα περιλαμβάνει επίσης τη διαδρομή του γονικού πακέτου:
>>> from concurrent.futures import process
>>> process.__name__
'concurrent.futures.process'
Ωστόσο, εάν το module εκτελείται στο περιβάλλον κώδικα ανωτέρου επιπέδου, το __name__ ορίζεται στη συμβολοσειρά '__main__'.
Τι είναι το «πεδίο ονομάτων ανωτέρου επιπέδου»;¶
Το __main__ είναι το όνομα του πεδίου ονομάτων όπου εκτελείται ο κώδικας ανωτέρου επιπέδου. Ο «κώδικας ανωτέρου επιπέδου» είναι η πρώτη μονάδα Python που καθορίζεται από τον χρήστη και αρχίζει να εκτελείται. Είναι «ανωτέρου επιπέδου» επειδή εισάγει όλα τα άλλα modules που χρειάζεται το πρόγραμμα. Μερικές φορές ο «κώδικας ανωτέρου επιπέδου» ονομάζεται σημείο εισόδου στην εφαρμογή.
Το πεδίο ονομάτων ανωτέρου επιπέδου μπορεί να είναι:
το πεδίο ονομάτων μιας διαδραστικής γραμμής εντολών:
>>> __name__ '__main__'
το Python module που δόθηκε στον διερμηνέα Python ως όρισμα αρχείου:
$ python helloworld.py Hello, world!
το Python module ή πακέτο που δόθηκε στον διερμηνέα Python με το όρισμα
-m:$ python -m tarfile usage: tarfile.py [-h] [-v] (...)
Κώδικας Python που διαβάζεται από τον διερμηνέα Python από την τυπική είσοδο:
$ echo "import this" | python The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. ...
Κώδικας Python που δόθηκε στον διερμηνέα Python με το όρισμα
-c:$ python -c "import this" The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. ...
Σε κάθε μία από αυτές τις περιπτώσεις, το __name__ του module ανωτέρου επιπέδου ορίζεται σε '__main__'.
Ως αποτέλεσμα, ένα module μπορεί να ανακαλύψει εάν εκτελείται στο πεδίο ονομάτων ανωτέρου επιπέδου ελέγχοντας το δικό του __name__, το οποίο επιτρέπει μια κοινή ιδιωματική έκφραση για την εκτέλεση κώδικα υπό όρους όταν το module δεν έχει αρχικοποιηθεί από μια δήλωση εισαγωγής:
if __name__ == '__main__':
# Execute when the module is not initialized from an import statement.
...
Δείτε επίσης
Για μια πιο λεπτομερή ματιά στο πώς ορίζεται το __name__ σε όλες τις περιπτώσεις, δείτε την ενότητα του εγχειριδίου Modules.
Χρήσεις με ιδιωματισμό¶
Ορισμένα modules περιέχουν κώδικα που προορίζεται μόνο για χρήση ως σενάριο, όπως η ανάλυση ορισμάτων γραμμής εντολών ή η λήψη δεδομένων από την τυπική είσοδο. Εάν ένα τέτοιο module εισαχθεί από ένα διαφορετικό module, για παράδειγμα για να ελεγχθεί με unit tests, ο κώδικας του σεναρίου θα εκτελούνταν επίσης κατά λάθος.
Εδώ είναι που γίνεται χρήσιμο το μπλοκ κώδικα if __name__ == '__main__'. Ο κώδικας μέσα σε αυτό το μπλοκ δεν θα εκτελεστεί εκτός εάν το module εκτελεστεί στο πεδίο ονομάτων ανωτέρου επιπέδου.
Η τοποθέτηση όσο το δυνατόν λιγότερων δηλώσεων στο μπλοκ κάτω από το if __name__ == '__main__' μπορεί να βελτιώσει την σαφήνεια και την ορθότητα του κώδικα. Στις περισσότερες περιπτώσεις, μια συνάρτηση με το όνομα main περικλείει την κύρια συμπεριφορά του προγράμματος:
# echo.py
import shlex
import sys
def echo(phrase: str) -> None:
"""A dummy wrapper around print."""
# for demonstration purposes, you can imagine that there is some
# valuable and reusable logic inside this function
print(phrase)
def main() -> int:
"""Echo the input arguments to standard output"""
phrase = shlex.join(sys.argv)
echo(phrase)
return 0
if __name__ == '__main__':
sys.exit(main()) # next section explains the use of sys.exit
Σημειώστε ότι εάν το module δεν περικλείει τον κώδικα μέσα στη συνάρτηση main αλλά τον τοποθετεί απευθείας μέσα στο μπλοκ if __name__ == '__main__', η μεταβλητή phrase θα είναι παγκόσμια για ολόκληρο το module. Αυτό είναι ευαίσθητο σε σφάλματα καθώς άλλες συναρτήσεις μέσα στο module θα μπορούσαν να χρησιμοποιούν κατά λάθος την καθολική μεταβλητή αντί για ένα τοπικό όνομα. Μια συνάρτηση main λύνει αυτό το πρόβλημα.
Χρησιμοποιώντας μια συνάρτηση main έχει το πρόσθετο όφελος ότι η συνάρτηση echo είναι απομονωμένη και μπορεί να εισαχθεί αλλού. Όταν το echo.py εισάγεται, οι συναρτήσεις echo και main θα οριστούν, αλλά καμία από αυτές δεν θα κληθεί, επειδή __name__ != '__main__'.
Παρατηρήσεις συσκευασίας¶
Οι συναρτήσεις main χρησιμοποιούνται συχνά για τη δημιουργία εργαλείων γραμμής εντολών καθορίζοντάς τες ως σημεία εισόδου για σενάρια κονσόλας. Όταν γίνεται αυτό, το pip εισάγει την κλήση της συνάρτησης σε ένα πρότυπο σενάριο, όπου η τιμή επιστροφής της main περνάει στη sys.exit(). Για παράδειγμα:
sys.exit(main())
Εφόσον η κλήση της main είναι περιτυλιγμένη στη sys.exit(), η προσδοκία είναι ότι η συνάρτησή σας θα επιστρέψει κάποια τιμή αποδεκτή ως είσοδο στη sys.exit()· συνήθως, έναν ακέραιο ή None (που επιστρέφεται αυτοματοποιημένα εάν η συνάρτησή σας δεν έχει δήλωση επιστροφής).
Ακολουθώντας προληπτικά αυτή τη σύμβαση, το module μας θα έχει την ίδια συμπεριφορά όταν εκτελείται απευθείας (δηλαδή python echo.py) όπως θα έχει εάν αργότερα το συσκευάσουμε ως σημείο εισόδου σε σενάριο κονσόλας σε ένα πακέτο που μπορεί να εγκατασταθεί με pip.
Ιδιαίτερα, να είστε προσεκτικοί/ές σχετικά με την επιστροφή συμβολοσειρών από τη δική σας συνάρτηση main. Το sys.exit() θα ερμηνεύσει ένα όρισμα συμβολοσειράς ως μήνυμα αποτυχίας, οπότε το πρόγραμμά σας θα έχει κωδικό εξόδου 1, που υποδεικνύει αποτυχία, και η συμβολοσειρά θα γραφεί στο sys.stderr. Το παράδειγμα echo.py από νωρίτερα αποτελεί παράδειγμα χρήσης της σύμβασης sys.exit(main()).
Δείτε επίσης
Το Python Packaging User Guide περιέχει μια συλλογή εγχειριδίων και αναφορών σχετικά με το πώς να διανείμετε και να εγκαθιστάτε πακέτα Python με σύγχρονα εργαλεία.
__main__.py στα πακέτα της Python¶
Εάν δεν είστε εξοικειωμένοι με τα πακέτα της Python, δείτε την ενότητα Πακέτα του εγχειριδίου. Συνήθως, το αρχείο __main__.py χρησιμοποιείται για να παρέχει μια διεπαφή γραμμής εντολών για ένα πακέτο. Σκεφτείτε το ακόλουθο υποθετικό πακέτο, «bandclass»:
bandclass
├── __init__.py
├── __main__.py
└── student.py
Το __main__.py θα εκτελεστεί όταν το πακέτο αυτό εκκινηθεί απευθείας από τη γραμμή εντολών χρησιμοποιώντας τη σημαία -m. Για παράδειγμα:
$ python -m bandclass
Αυτή η εντολή θα προκαλέσει την εκτέλεση του __main__.py. Ο τρόπος με τον οποίο θα χρησιμοποιήσετε αυτόν τον μηχανισμό θα εξαρτηθεί από τη φύση του πακέτου που γράφετε, αλλά σε αυτή την υποθετική περίπτωση, θα είχε νόημα να επιτρέψετε στον δάσκαλο να αναζητήσει μαθητές:
# bandclass/__main__.py
import sys
from .student import search_students
student_name = sys.argv[1] if len(sys.argv) >= 2 else ''
print(f'Found student: {search_students(student_name)}')
Σημειώστε ότι το from .student import search_students είναι ένα παράδειγμα σχετικής εισαγωγής. Αυτό το στυλ εισαγωγής μπορεί να χρησιμοποιηθεί όταν αναφέρεστε σε modules εντός ενός πακέτου. Για περισσότερες λεπτομέρειες, δείτε το Intra-package αναφορές στην ενότητα Modules του εγχειριδίου.
Χρήσεις με ιδιωματισμό¶
Το περιεχόμενο του __main__.py συνήθως δεν περικλείεται με ένα μπλοκ if __name__ == '__main__'. Αντίθετα, αυτά τα αρχεία διατηρούνται σύντομα και εισάγουν συναρτήσεις για εκτέλεση από άλλα modules. Αυτά τα άλλα αρχεία μονάδων μπορούν στη συνέχεια να ελεγχθούν εύκολα με unit tests και είναι κατάλληλα επαναχρησιμοποιήσιμα.
Εάν χρησιμοποιηθεί, ένα μπλοκ if __name__ == '__main__' θα λειτουργήσει όπως αναμένεται για ένα αρχείο __main__.py μέσα σε ένα πακέτο, επειδή το χαρακτηριστικό του __name__ θα περιλαμβάνει τη διαδρομή του πακέτου εάν εισαχθεί:
>>> import asyncio.__main__
>>> asyncio.__main__.__name__
'asyncio.__main__'
Αυτό δεν θα λειτουργήσει όμως για αρχεία __main__.py στον ριζικό κατάλογο ενός αρχείου .zip. Επομένως, για συνέπεια, προτιμάται ένα ελάχιστο __main__.py χωρίς έλεγχο __name__.
Δείτε επίσης
Δείτε το venv για ένα παράδειγμα πακέτου με ένα ελάχιστο __main__.py στη βιβλιοθήκη προτύπων. Δεν περιέχει μπλοκ if __name__ == '__main__'. Μπορείτε να το εκκινήσετε με python -m venv [directory].
Δείτε το runpy για περισσότερες λεπτομέρειες σχετικά με τη σημαία -m για το εκτελέσιμο του διερμηνέα.
Δείτε το zipapp για το πώς να εκτελέσετε εφαρμογές που συσκευάζονται ως αρχεία .zip. Σε αυτή την περίπτωση, η Python αναζητά ένα αρχείο __main__.py στον ριζικό κατάλογο του αρχείου.
import __main__¶
Ανεξάρτητα από το ποιο module ξεκίνησε ένα πρόγραμμα Python, άλλα modules που εκτελούνται μέσα σε αυτό το ίδιο πρόγραμμα μπορούν να εισάγουν το πεδίο ονομάτων ανωτέρου επιπέδου (namespace) εισάγοντας το module __main__. Αυτό δεν εισάγει ένα αρχείο __main__.py αλλά μάλλον όποιο module έλαβε το ειδικό όνομα '__main__'.
Εδώ είναι ένα παράδειγμα module που χρησιμοποιεί το πεδίο ονομάτων __main__:
# namely.py
import __main__
def did_user_define_their_name():
return 'my_name' in dir(__main__)
def print_user_name():
if not did_user_define_their_name():
raise ValueError('Define the variable `my_name`!')
print(__main__.my_name)
Ένα παράδειγμα χρήσης αυτού του module θα μπορούσε να είναι το εξής:
# start.py
import sys
from namely import print_user_name
# my_name = "Dinsdale"
def main():
try:
print_user_name()
except ValueError as ve:
return str(ve)
if __name__ == "__main__":
sys.exit(main())
Τώρα, εάν ξεκινήσουμε το πρόγραμμά μας, το αποτέλεσμα θα μοιάζει με αυτό:
$ python start.py
Define the variable `my_name`!
Ο κωδικός εξόδου του προγράμματος θα ήταν 1, υποδεικνύοντας ένα σφάλμα. Αποσχολιάζοντας τη γραμμή my_name = "Dinsdale" διορθώνεται το πρόγραμμα και τώρα τερματίζει με κωδικό κατάστασης 0, υποδεικνύοντας επιτυχία:
$ python start.py
Dinsdale
Σημειώστε ότι η εισαγωγή του __main__ δεν προκαλεί κανένα πρόβλημα με την ακούσια εκτέλεση κώδικα ανωτέρου επιπέδου που προορίζεται για χρήση σε σενάριο και τοποθετείται στο μπλοκ if __name__ == "__main__" του module start. Γιατί λειτουργεί αυτό;
Η Python εισάγει ένα κενό module __main__ στο sys.modules κατά την εκκίνηση του διερμηνέα και το γεμίζει εκτελώντας κώδικα ανωτέρου επιπέδου. Στο παράδειγμά μας, αυτό είναι το module start που εκτελείται γραμμή-γραμμή και εισάγει το namely. Με τη σειρά του, το namely εισάγει το __main__ (που είναι πραγματικά το start). Αυτό είναι ένας κύκλος εισαγωγής! Ευτυχώς, επειδή το μερικώς γεμάτο module __main__ είναι παρόν στο sys.modules, η Python το περνάει στο namely. Δείτε Special considerations for __main__ στην αναφορά του συστήματος εισαγωγής για λεπτομέρειες σχετικά με το πώς λειτουργεί.
Το REPL της Python είναι ένα ακόμη παράδειγμα «πεδίου ονομάτων ανωτέρου επιπέδου», οπότε οτιδήποτε οριστεί στο REPL γίνεται μέρος του πεδίου ονομάτων __main__:
>>> import namely
>>> namely.did_user_define_their_name()
False
>>> namely.print_user_name()
Traceback (most recent call last):
...
ValueError: Define the variable `my_name`!
>>> my_name = 'Jabberwocky'
>>> namely.did_user_define_their_name()
True
>>> namely.print_user_name()
Jabberwocky
Το πεδίο ονομάτων __main__ χρησιμοποιείται στην υλοποίηση των pdb και rlcompleter.