5. Το σύστημα εισαγωγής¶
Ο κώδικας Python σε ένα module αποκτά πρόσβαση στον κώδικα ενός άλλου module μέσω της διαδικασίας του importing. Η δήλωση import είναι ο πιο συνηθισμένος τρόπος ενεργοποίησης του μηχανισμού import, αλλά δεν είναι ο μόνος. Συναρτήσεις όπως η importlib.import_module() και η ενσωματωμένη __import__() μπορούν επίσης να χρησιμοποιηθούν για την ενεργοποίηση του μηχανισμού εισαγωγής.
Η δήλωση import συνδυάζει δύο λειτουργίες· αναζητά το module με το συγκεκριμένο όνομα και στη συνέχεια συνδέει τα αποτελέσματα αυτής της αναζήτησης σε ένα όνομα στο τοπικό scope. Η λειτουργία αναζήτησης της δήλωσης import ορίζεται ως κλήση της συνάρτησης __import__(), με τα κατάλληλα ορίσματα. Η τιμή επιστροφής της __import__() χρησιμοποιείται για να εκτελεστεί η λειτουργία δέσμευσης ονόματος της δήλωσης import. Δείτε τη δήλωση import για τις ακριβείς λεπτομέρειες αυτής της λειτουργίας.
Μια άμεση κλήση της __import__() εκτελεί μόνο την αναζήτηση του module και, αν βρεθεί, τη λειτουργία δημιουργίας του. Παρότι μπορεί να προκύψουν ορισμένες παρενέργειες, όπως η εισαγωγή των γονικών πακέτων και η ενημέρωση διάφορων προσωρινών μνημών (συμπεριλαμβανομένου του sys.modules), μόνο η δήλωση import εκτελεί λειτουργία δέσμευσης ονόματος.
Όταν εκτελείται μια δήλωση import, καλείται η τυπική ενσωματωμένη συνάρτηση __import__(). Άλλοι μηχανισμοί ενεργοποίησης του συστήματος εισαγωγής (όπως η importlib.import_module()) μπορεί να επιλέξουν να παρακάμψουν τη __import__() και να χρησιμοποιήσουν δικές τους λύσεις για την υλοποίηση της σημασιολογίας της εισαγωγής.
Όταν εισάγεται ένα module για πρώτη φορά, η Python το αναζητά και, αν το βρει, δημιουργεί ένα αντικείμενο module [1], αρχικοποιώντας το. Αν το module με το συγκεκριμένο όνομα δεν μπορεί να βρεθεί, γίνεται raise η εξαίρεση ModuleNotFoundError. Η Python υλοποιεί διάφορες στρατηγικές αναζήτησης για το module όταν ενεργοποιείται ο μηχανισμός εισαγωγής. Αυτές οι στρατηγικές μπορούν να τροποποιηθούν και να επεκταθούν μέσω διάφορων hooks που περιγράφονται στις παρακάτω ενότητες.
Άλλαξε στην έκδοση 3.3: Το σύστημα εισαγωγής έχει ενημερωθεί ώστε να υλοποιεί πλήρως τη δεύτερη φάση του PEP 302. Δεν υπάρχει πλέον κανένας έμμεσος μηχανισμός εισαγωγής - ο πλήρης μηχανισμός εισαγωγής εκτίθεται μέσω του sys.meta_path. Επιπλέον, έχει υλοποιηθεί εγγενής υποστήριξη για namespace packages (βλ. PEP 420).
5.1. importlib¶
Το module importlib παρέχει ένα πλούσιο API για αλληλεπίδραση με το σύστημα εισαγωγής. Για παράδειγμα, η importlib.import_module() παρέχει ένα προτεινόμενο, απλούστερο API από την ενσωματωμένη __import__() για την ενεργοποίηση του μηχανισμού εισαγωγής. Ανατρέξτε στην τεκμηρίωση της βιβλιοθήκης importlib για περισσότερες λεπτομέρειες.
5.2. Πακέτα¶
Η Python έχει μόνο έναν τύπο αντικειμένου module, και όλα τα modules είναι αυτού του τύπου, ανεξάρτητα από το αν το module υλοποιείται σε Python, C ή κάτι άλλο. Για να βοηθήσει στην οργάνωση των modules και να παρέχει μια ιεραρχία ονοματοδοσίας, η Python διαθέτει την έννοια των packages.
Μπορείτε να σκεφτείτε τα packages ως φακέλους σε ένα σύστημα αρχείων και τα modules ως αρχεία μέσα σε φακέλους, αλλά μην πάρετε αυτή την αναλογία κυριολεκτικά, καθώς packages και modules δεν είναι απαραίτητο να προέρχονται από το σύστημα αρχείων. Για τους σκοπούς αυτής της τεκμηρίωσης, θα χρησιμοποιήσουμε αυτή τη βολική αναλογία φακέλων και αρχείων. Όπως οι φάκελοι του συστήματος αρχείων, τα packages οργανώνονται ιεραρχικά, και μπορούν να περιέχουν subpackages, καθώς και κανονικά modules.
Είναι σημαντικό να έχετε υπόψη ότι όλα τα packages είναι modules, αλλά δεν είναι όλα τα modules packages. Με άλλα λόγια, τα packages είναι απλώς ένας ειδικός τύπος module. Συγκεκριμένα, οποιοδήποτε module περιέχει το χαρακτηριστικό __path__ θεωρείται package.
Όλα τα modules έχουν ένα όνομα. Τα ονόματα των subpackages διαχωρίζονται από το όνομα του γονικού package με τελεία, αντίστοιχα με τη συνηθισμένη σύνταξη πρόσβασης σε χαρακτηριστικά στην Python. Έτσι, μπορεί να έχετε ένα package με όνομα email, το οποίο με τη σειρά του έχει ένα subpackage με όνομα email.mime και ένα module μέσα σε αυτό το subpackage με όνομα email.mime.text.
5.2.1. Κανονικά πακέτα¶
Η Python ορίζει δύο τύπους packages: τα regular packages και τα namespace packages. Τα regular packages είναι τα παραδοσιακά packages όπως υπήρχαν στην Python 3.2 και παλαιότερα. Ένα regular package υλοποιείται συνήθως ως φάκελος που περιέχει ένα αρχείο __init__.py. Όταν εισάγεται ένα regular package, το αρχείο __init__.py εκτελείται έμμεσα, και τα αντικείμενα που ορίζει δεσμεύονται σε ονόματα στο namespace του package. Το __init__.py μπορεί να περιέχει τον ίδιο κώδικα Python που μπορεί να περιέχει οποιοδήποτε άλλο module, και η Python θα προσθέσει ορισμένα επιπλέον χαρακτηριστικά στο module όταν εισάγεται.
Για παράδειγμα, η ακόλουθη διάταξη στο σύστημα αρχείων ορίζει ένα package ανωτέρου επιπέδου parent με τρία subpackages:
parent/
__init__.py
one/
__init__.py
two/
__init__.py
three/
__init__.py
Η εισαγωγή του parent.one θα εκτελέσει έμμεσα τα parent/__init__.py και parent/one/__init__.py. Μετέπειτα εισαγωγές του parent.two ή του parent.three θα εκτελέσουν τα parent/two/__init__.py και parent/three/__init__.py αντίστοιχα.
5.2.2. Namespace πακέτα¶
Ένα namespace πακέτο είναι ένας συνδυασμός από διάφορα portions, όπου κάθε portion συνεισφέρει ένα subpackage στο γονικό πακέτο. Τα portions μπορεί να βρίσκονται σε διαφορετικές τοποθεσίες στο σύστημα αρχείων. Μπορεί επίσης να βρίσκονται σε zip αρχεία, στο δίκτυο ή οπουδήποτε αλλού αναζητά η Python κατά την εισαγωγή. Τα namespace πακέτα μπορεί να αντιστοιχούν ή να μην αντιστοιχούν άμεσα σε αντικείμενα στο σύστημα αρχείων· μπορεί να είναι «εικονικά» modules που δεν έχουν συγκεκριμένη φυσική αναπαράσταση.
Τα namespace πακέτα δεν χρησιμοποιούν μια συνηθισμένη λίστα για το χαρακτηριστικό __path__. Αντί γι’ αυτό χρησιμοποιούν έναν προσαρμοσμένο iterable τύπο, ο οποίος θα εκτελέσει αυτόματα μία νέα αναζήτηση για portions του πακέτου στην επόμενη προσπάθεια εισαγωγής μέσα σε αυτό το πακέτο, αν αλλάξει το path του γονικού πακέτου (ή το sys.path για πακέτο ανωτέρου επιπέδου).
Με τα namespace πακέτα, δεν υπάρχει αρχείο parent/__init__.py. Στην πράξη, μπορεί να βρεθούν πολλοί φάκελοι parent κατά την αναζήτηση εισαγωγής, όπου ο καθένας παρέχεται από διαφορετικό portion. Έτσι, το parent/one ενδέχεται να μην βρίσκεται φυσικά δίπλα στο parent/two. Σε αυτή την περίπτωση, η Python θα δημιουργήσει ένα namespace πακέτο για το πακέτο ανωτέρου επιπέδου parent κάθε φορά που γίνεται εισαγωγή αυτού ή κάποιου από τα subpackages του.
Δείτε επίσης το PEP 420 για την προδιαγραφή των namespace πακέτων.
5.3. Αναζήτηση¶
Για να ξεκινήσει η αναζήτηση, η Python χρειάζεται το πλήρως αναγνωρισμένο όνομα του module (ή του πακέτου, αλλά για τους σκοπούς αυτής της συζήτησης η διαφορά δεν έχει σημασία) που εισάγεται. Αυτό το όνομα μπορεί να προέρχεται από διάφορα ορίσματα της δήλωσης import ή από τις παραμέτρους των συναρτήσεων importlib.import_module() ή __import__().
Αυτό το όνομα θα χρησιμοποιηθεί σε διάφορες φάσεις της αναζήτησης import και μπορεί να είναι μια διαδρομή με τελείες προς ένα submodule, π.χ. foo.bar.baz. Σε αυτή την περίπτωση, η Python πρώτα προσπαθεί να εισάγει το foo, μετά το foo.bar και τέλος το foo.bar.baz. Αν αποτύχει κάποιες από τις ενδιάμεσες εισαγωγές, γίνεται raise η ModuleNotFoundError.
5.3.1. Η προσωρινή μνήμη του module¶
Το πρώτο σημείο που ελέγχεται κατά την αναζήτηση εισαγωγής είναι το sys.modules. Αυτή η αντιστοίχιση λειτουργεί ως προσωρινή μνήμη όλων των modules που έχουν εισαχθεί στο παρελθόν, συμπεριλαμβανομένων των ενδιάμεσων διαδρομών. Έτσι, αν το foo.bar.baz είχε εισαχθεί προηγουμένως, το sys.modules θα περιέχει εγγραφές για τα foo, foo.bar και foo.bar.baz. Κάθε κλειδί θα έχει ως τιμή το αντίστοιχο αντικείμενο module.
Κατά την εισαγωγή, το όνομα του module αναζητείται στο sys.modules και, αν υπάρχει, η συσχετισμένη τιμή είναι το module που ικανοποιεί την εισαγωγή και η διαδικασία ολοκληρώνεται. Ωστόσο, αν η τιμή είναι None, τότε γίνεται raise το ModuleNotFoundError. Αν το όνομα του module λείπει, η Python θα συνεχίσει να αναζητά το module.
Το sys.modules είναι εγγράψιμο. Η διαγραφή ενός κλειδιού μπορεί να μην καταστρέψει το αντίστοιχο module (καθώς άλλα modules μπορεί να κρατούν αναφορές σε αυτό), αλλά θα ακυρώσει την εγγραφή της προσωρινής μνήμης για το module με το συγκεκριμένο όνομα, προκαλώντας την Python να το αναζητήσει ξανά στην επόμενο εισαγωγή. Το κλειδί μπορεί επίσης να οριστεί σε None, αναγκάζοντας την επόμενο εισαγωγή του module να καταλήξει σε ModuleNotFoundError.
Προσοχή όμως: αν κρατήσετε μια αναφορά στο αντικείμενο module, ακυρώσετε την εγγραφή του στην προσωρινή μνήμη του sys.modules και μετά ξανακάνετε εισαγωγή το module με το ίδιο όνομα, τα δύο αντικείμενα module δεν θα είναι τα ίδια. Αντίθετα, η importlib.reload() θα επαναχρησιμοποιήσει το ίδιο αντικείμενο module και απλώς θα επαναρχικοποιήσει τα περιεχόμενά του εκτελώντας ξανά τον κώδικα του module.
5.3.2. Finders και loaders¶
Αν το module με το συγκεκριμένο όνομα δεν βρεθεί στο sys.modules, τότε ενεργοποιείται το πρωτόκολλο εισαγωγής της Python για να βρεθεί και να φορτωθεί το module. Αυτό το πρωτόκολλο αποτελείται από δύο εννοιολογικά αντικείμενα: finders και loaders. Ο ρόλος ενός finder είναι να καθορίσει αν μπορεί να βρει το module με το συγκεκριμένο όνομα, χρησιμοποιώντας όποια στρατηγική γνωρίζει. Αντικείμενα που υλοποιούν και τις δύο αυτές διεπαφές αναφέρονται ως importers - επιστρέφουν τον εαυτό τους όταν διαπιστώσουν ότι μπορούν να φορτώσουν το ζητούμενο module.
Η Python περιλαμβάνει αρκετούς προεπιλεγμένους finders και importers. Το πρώτο γνωρίζει πώς να εντοπίζει built-in modules, και το δεύτερο πώς να εντοπίζει παγωμένα (frozen) modules. Ένας τρίτος προεπιλεγμένος finder αναζητά modules σε ένα import path. Το import path είναι μια λίστα από τοποθεσίες που μπορεί να είναι διαδρομές του συστήματος αρχείων ή zip αρχεία. Μπορεί επίσης να επεκταθεί ώστε να αναζητά οποιονδήποτε εντοπίσιμο πόρο, όπως αυτούς που προσδιορίζονται από URLs.
Ο μηχανισμός εισαγωγής είναι επεκτάσιμος, επομένως μπορούν να προστεθούν νέοι finders ώστε να επεκταθεί το εύρος και η κάλυψη της αναζήτησης των modules.
Οι finders δεν φορτώνουν πραγματικά τα modules. Αν μπορούν να βρουν το module με το συγκεκριμένο όνομα, επιστρέφουν ένα module spec, δηλαδή μια ενθυλάκωση της πληροφορίας που σχετίζεται με την εισαγωγή του module, την οποία ο μηχανισμός εισαγωγής χρησιμοποιεί στη συνέχεια για να φορτώσει το module.
Οι ακόλουθες ενότητες περιγράφουν πιο αναλυτικά το πρωτόκολλο για finders και loaders, συμπεριλαμβανομένου του πώς μπορείτε να δημιουργήσετε και να καταχωρίσετε νέους, ώστε να επεκτείνετε τον μηχανισμό εισαγωγής.
Άλλαξε στην έκδοση 3.4: Σε παλαιότερες εκδόσεις της Python, οι finders επέστρεφαν απευθείας loaders, ενώ τώρα επιστρέφουν module specs που περιέχουν loaders. Οι loaders εξακολουθούν να χρησιμοποιούνται κατά την εισαγωγή, αλλά έχουν λιγότερες ευθύνες.
5.3.3. Εισαγωγή hooks¶
Ο μηχανισμός εισαγωγής είναι σχεδιασμένος ώστε να είναι επεκτάσιμος· ο βασικός μηχανισμός για αυτό είναι η εισαγωγή hooks. Υπάρχουν δύο τύποι εισαγωγής hooks: meta hooks και import path hooks.
Τα meta hooks καλούνται στην αρχή της επεξεργασίας εισαγωγής, πριν συμβεί οποιαδήποτε άλλη επεξεργασία εισαγωγής, εκτός από την αναζήτηση στην προσωρινή μνήμη του sys.modules. Αυτό επιτρέπει στα meta hooks να υπερκαλύπτουν την επεξεργασία του sys.path, τα παγωμένα modules ή ακόμη και τα built-in modules. Τα meta hooks καταχωρίζονται προσθέτοντας νέα αντικείμενα finder στο sys.meta_path, όπως περιγράφεται παρακάτω.
Τα import path hooks καλούνται ως μέρος της επεξεργασίας του sys.path (ή του package.__path__), στο σημείο όπου συναντάται το αντίστοιχο στοιχείο διαδρομής. Τα import path hooks καταχωρίζονται προσθέτοντας νέα callables στο sys.path_hooks, όπως περιγράφεται παρακάτω.
5.3.4. Το meta path¶
Όταν το module με το συγκεκριμένο όνομα δεν βρεθεί στο sys.modules, η Python στη συνέχεια το αναζητά στο sys.meta_path, το οποίο περιέχει μια λίστα από meta path finder αντικείμενα. Αυτοί οι finders ερωτώνται με τη σειρά για να διαπιστωθεί αν γνωρίζουν πώς να χειριστούν το module. Οι meta path finders πρέπει να υλοποιούν μια μέθοδο με όνομα find_spec(), η οποία λαμβάνει τρία ορίσματα: ένα όνομα, ένα import path και (προαιρετικά) ένα target module. Ο meta path finder μπορεί να χρησιμοποιήσει οποιαδήποτε στρατηγική θέλει για να καθορίσει αν μπορεί να χειριστεί το module ή όχι.
Αν ο meta path finder γνωρίζει πώς να χειριστεί το module, επιστρέφει ένα spec αντικείμενο. Αν δεν μπορεί να χειριστεί το module, επιστρέφει None. Αν η επεξεργασία του sys.meta_path φτάσει στο τέλος της λίστας χωρίς να επιστραφεί spec, τότε γίνεται raise το ModuleNotFoundError. Οποιεσδήποτε άλλες εξαιρέσεις γίνονται raise απλώς προωθούνται προς τα πάνω, τερματίζοντας τη διαδικασία εισαγωγής.
Η μέθοδος find_spec() των meta path finders καλείται με δύο ή τρία ορίσματα. Το πρώτο είναι το πλήρως αναγνωρισμένο όνομα του module που εισάγεται, π.χ. foo.bar.baz. Το δεύτερο όρισμα είναι οι καταχωρήσεις διαδρομής που θα χρησιμοποιηθούν για την αναζήτηση του module. Για modules ανωτέρου επιπέδου, το δεύτερο όρισμα είναι None, αλλά για submodules ή subpackages, είναι η τιμή του χαρακτηριστικού __path__ του γονικού πακέτου. Αν δεν είναι δυνατή η πρόσβαση στο κατάλληλο __path__, γίνεται raise το ModuleNotFoundError. Το τρίτο όρισμα είναι ένα υπάρχον αντικείμενο module που θα αποτελέσει αργότερα τον στόχο της φόρτωσης. Το σύστημα εισαγωγής περνά target module μόνο κατά την επαναφόρτωση.
Το meta path μπορεί να διασχιστεί πολλές φορές για ένα μόνο αίτημα εισαγωγής. Για παράδειγμα, υποθέτοντας ότι κανένα από τα εμπλεκόμενα modules δεν έχει ήδη προστεθεί στην προσωρινή μνήμη, η εισαγωγή του foo.bar.baz θα εκτελέσει πρώτα μία εισαγωγή ανωτέρου επιπέδου, καλώντας το mpf.find_spec("foo", None, None) σε κάθε meta path finder (mpf). Αφού εισαχθεί το foo, το foo.bar θα εισαχθεί διασχίζοντας το meta path για δεύτερη φορά, καλώντας την mpf.find_spec("foo.bar", foo.__path__, None). Μόλις γίνει η εισαγωγή του foo.bar, η τελική διάσχιση θα καλέσει το mpf.find_spec("foo.bar.baz", foo.bar.__path__, None).
Ορισμένοι meta path finders υποστηρίζουν μόνο εισαγωγές ανωτέρου επιπέδου. Αυτοί οι importers θα επιστρέφουν πάντα None όταν ως δεύτερο όρισμα περάσει οτιδήποτε άλλο εκτός από None.
Το προεπιλεγμένο sys.meta_path της Python έχει τρεις meta path finders: έναν που γνωρίζει πώς να εισάγει ενσωματωμένα modules, έναν που γνωρίζει πώς να εισάγει παγωμένα modules, και έναν που γνωρίζει πώς να εισάγει modules από ένα import path (δηλ. τον path based finder).
Άλλαξε στην έκδοση 3.4: Η μέθοδος find_spec() των meta path finders αντικατέστησε τη find_module(), η οποία πλέον χαρακτηριστεί ως απαρχαιωμένη. Αν και θα συνεχίσει να λειτουργεί χωρίς αλλαγές, ο μηχανισμός εισαγωγής θα τη δοκιμάζει μόνο αν ο finder δεν υλοποιεί τη find_spec().
Άλλαξε στην έκδοση 3.10: Η χρήση της find_module() από το σύστημα εισαγωγής πλέον κάνει raise την ImportWarning.
Άλλαξε στην έκδοση 3.12: Η find_module() έχει αφαιρεθεί. Χρησιμοποιήστε την find_spec() στη θέση της.
5.4. Φόρτωση¶
Αν και όταν βρεθεί ένα module spec, ο μηχανισμός εισαγωγής θα το χρησιμοποιήσει (και τον loader που περιέχει) για να φορτώσει το module. Ακολουθεί μια προσέγγιση του τι συμβαίνει στο στάδιο φόρτωσης του import:
module = None
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
# It is assumed 'exec_module' will also be defined on the loader.
module = spec.loader.create_module(spec)
if module is None:
module = ModuleType(spec.name)
# The import-related module attributes get set here:
_init_module_attrs(spec, module)
if spec.loader is None:
# unsupported
raise ImportError
if spec.origin is None and spec.submodule_search_locations is not None:
# namespace package
sys.modules[spec.name] = module
elif not hasattr(spec.loader, 'exec_module'):
module = spec.loader.load_module(spec.name)
else:
sys.modules[spec.name] = module
try:
spec.loader.exec_module(module)
except BaseException:
try:
del sys.modules[spec.name]
except KeyError:
pass
raise
return sys.modules[spec.name]
Σημειώστε τις ακόλουθες λεπτομέρειες:
Αν υπάρχει ήδη ένα αντικείμενο module με το συγκεκριμένο όνομα στο
sys.modules, το import θα το έχει ήδη επιστρέψει.Το module θα υπάρχει στο
sys.modulesπριν ο loader εκτελέσει τον κώδικα του module. Αυτό είναι κρίσιμο, επειδή ο κώδικας του module μπορεί (άμεσα ή έμμεσα) να εισάγει τον εαυτό του· η προσθήκη του στοsys.modulesεκ των προτέρων αποτρέπει ατέρμονη αναδρομή στη χειρότερη περίπτωση και πολλαπλή φόρτωση στην καλύτερη.Αν η φόρτωση αποτύχει, το module που απέτυχε – και μόνο αυτό – αφαιρείται από το
sys.modules. Οποιοδήποτε module βρίσκεται ήδη στην προσωρινή μνήμη τουsys.modules, καθώς και οποιοδήποτε module φορτώθηκε επιτυχώς ως παρενέργεια, πρέπει να παραμείνει στην προσωρινή μνήμη. Αυτό έρχεται σε αντίθεση με την επαναφόρτωση, όπου ακόμη και το module που αποτυγχάνει παραμένει στοsys.modules.Αφού δημιουργηθεί το module αλλά πριν την εκτέλεση, ο μηχανισμός εισαγωγής ορίζει τα χαρακτηριστικά του module που σχετίζονται με την εισαγωγή («_init_module_attrs» στο παραπάνω παράδειγμα ψευδοκώδικα), όπως συνοψίζεται σε μια μεταγενέστερη ενότητα.
Η εκτέλεση του module είναι η κρίσιμη στιγμή της φόρτωσης κατά την οποία γεμίζει το namespace του module. Η εκτέλεση ανατίθεται εξ ολοκλήρου στον loader, ο οποίος αποφασίζει τι θα συμπληρωθεί και με ποιον τρόπο.
Το module που δημιουργείται κατά τη φόρτωση και περνά στη exec_module() ενδέχεται να μην είναι αυτό που επιστρέφεται στο τέλος του import [2].
Άλλαξε στην έκδοση 3.4: Το σύστημα εισαγωγής έχει αναλάβει τις τυποποιημένες ευθύνες των loaders. Αυτές εκτελούνταν παλαιότερα από τη μέθοδο importlib.abc.Loader.load_module().
5.4.1. Loaders¶
Οι module loaders παρέχουν την κρίσιμη λειτουργία της φόρτωσης: την εκτέλεση του module. Ο μηχανισμός εισαγωγής καλεί τη μέθοδο importlib.abc.Loader.exec_module() με ένα μόνο όρισμα, το αντικείμενο module που θα εκτελεστεί. Οποιαδήποτε τιμή επιστρέφει η exec_module() αγνοείται.
Οι loaders πρέπει να ικανοποιούν τις ακόλουθες απαιτήσεις:
Αν το module είναι Python module (σε αντίθεση με ένα ενσωματωμένο module ή δυναμικά φορτωμένη επέκταση), ο loader θα πρέπει να εκτελέσει τον κώδικα του module στο καθολικό namespace του module (
module.__dict__).Αν ο loader δεν μπορεί να εκτελέσει το module, θα πρέπει να κάνει raise το
ImportError, αν και οποιαδήποτε άλλη εξαίρεση γίνει raise κατά τηexec_module()θα προωθηθεί προς τα πάνω.
Σε πολλές περιπτώσεις, ο finder και ο loader μπορεί να είναι το ίδιο αντικείμενο· σε αυτές τις περιπτώσεις, η μέθοδος find_spec() απλώς θα επιστρέψει ένα spec με τον loader ορισμένο σε self.
Οι module loaders μπορούν, προαιρετικά, να δημιουργούν το αντικείμενο module κατά τη φόρτωση υλοποιώντας μια μέθοδο create_module(). Παίρνει ένα όρισμα, το module spec, και επιστρέφει το νέο αντικείμενο module που θα χρησιμοποιηθεί κατά τη φόρτωση. Η create_module() δεν χρειάζεται να ορίσει χαρακτηριστικά στο αντικείμενο module. Αν η μέθοδος επιστρέψει None, ο μηχανισμός εισαγωγής θα δημιουργήσει ο ίδιος το νέο module.
Added in version 3.4: Η μέθοδος create_module() των loaders.
Άλλαξε στην έκδοση 3.4: Η μέθοδος load_module() αντικαταστάθηκε από την exec_module() και ο μηχανισμός εισαγωγής ανέλαβε όλες τις τυποποιημένες ευθύνες της φόρτωσης.
Για συμβατότητα με υπάρχοντες loaders, ο μηχανισμός εισαγωγής θα χρησιμοποιήσει τη μέθοδο load_module() αν υπάρχει και ο loader δεν υλοποιεί την exec_module(). Ωστόσο, η load_module() είναι απαρχαιωμένη και οι loaders θα πρέπει να υλοποιούν την exec_module().
Η μέθοδος load_module() πρέπει να υλοποιεί όλη την τυποποιημένη λειτουργικότητα φόρτωσης που περιγράφεται παραπάνω, επιπλέον της εκτέλεσης του module. Ισχύουν οι ίδιοι περιορισμοί, με κάποιες επιπλέον διευκρινίσεις:
Αν υπάρχει ήδη αντικείμενο module με το συγκεκριμένο όνομα στο
sys.modules, ο loader πρέπει να χρησιμοποιήσει αυτό το υπάρχον module. (Διαφορετικά, ηimportlib.reload()δεν θα λειτουργεί σωστά.) Αν το module με το συγκεκριμένο όνομα δεν υπάρχει στοsys.modules, ο loader πρέπει να δημιουργήσει νέο αντικείμενο module και να το προσθέσει στοsys.modules.Το module πρέπει να υπάρχει στο
sys.modulesπριν ο loader εκτελέσει τον κώδικα του module, ώστε να αποτραπεί ατέρμονη αναδρομή ή πολλαπλή φόρτωση.Αν η φόρτωση αποτύχει, ο loader πρέπει να αφαιρέσει οποιαδήποτε modules έχει εισάγει στο
sys.modules, αλλά πρέπει να αφαιρέσει μόνο το/τα module(s) που απέτυχαν, και μόνο αν ο loader έχει φορτώσει τα module(s) αυτά ρητά.
Άλλαξε στην έκδοση 3.5: Γίνεται raise η DeprecationWarning όταν ορίζεται η exec_module() αλλά δεν ορίζεται η create_module().
Άλλαξε στην έκδοση 3.6: Γίνεται raise το ImportError όταν ορίζεται η exec_module() αλλά δεν ορίζεται η create_module().
Άλλαξε στην έκδοση 3.10: Η χρήση της load_module() θα κάνει raise την ImportWarning.
5.4.2. Submodules¶
Όταν ένα submodule φορτώνεται με οποιονδήποτε μηχανισμό (π.χ. importlib APIs, τις δηλώσεις import ή import-from, ή την ενσωματωμένη __import__()), δημιουργείται μια δέσμευση στο namespace του γονικού module προς το αντικείμενο του submodule. Για παράδειγμα, αν το πακέτο spam έχει submodule foo, μετά την εισαγωγή του spam.foo, το spam θα έχει ένα χαρακτηριστικό foo που είναι δεσμευμένο στο submodule. Ας πούμε ότι έχετε την ακόλουθη δομή φακέλων:
spam/
__init__.py
foo.py
και το spam/__init__.py περιέχει την ακόλουθη γραμμή:
from .foo import Foo
τότε, η εκτέλεση των παρακάτω δημιουργεί δεσμεύσεις ονομάτων για τα foo και Foo στο module spam:
>>> import spam
>>> spam.foo
<module 'spam.foo' from '/tmp/imports/spam/foo.py'>
>>> spam.Foo
<class 'spam.foo.Foo'>
Με δεδομένους τους γνώριμους κανόνες δέσμευσης ονομάτων της Python, αυτό μπορεί να φαίνεται παράξενο, αλλά στην πραγματικότητα είναι θεμελιώδες χαρακτηριστικό του συστήματος εισαγωγής. Η αμετάβλητη συνθήκη είναι ότι, αν έχετε sys.modules['spam'] και sys.modules['spam.foo'] (όπως θα είχατε μετά την παραπάνω εισαγωγή), το δεύτερο πρέπει να εμφανίζεται ως το χαρακτηριστικό foo του πρώτου.
5.4.3. Module specs¶
Ο μηχανισμός εισαγωγής χρησιμοποιεί ποικιλία πληροφοριών για κάθε module κατά την εισαγωγή, ειδικά πριν τη φόρτωση. Οι περισσότερες από αυτές τις πληροφορίες είναι κοινές για όλα τα modules. Ο σκοπός του spec ενός module είναι να ενθυλακώσει αυτή την πληροφορία που σχετίζεται με την εισαγωγή, σε βάση ανά module.
Η χρήση ενός spec κατά την εισαγωγή επιτρέπει τη μεταφορά κατάστασης μεταξύ των συστατικών του συστήματος εισαγωγής, π.χ. μεταξύ του finder που δημιουργεί το module spec και του loader που το εκτελεί. Το σημαντικότερο είναι ότι επιτρέπει στον μηχανισμό εισαγωγής να εκτελεί τις τυποποιημένες λειτουργίες φόρτωσης, ενώ χωρίς module spec αυτή η ευθύνη ανήκε στον loader.
Το spec του module εκτίθεται ως module.__spec__. Η κατάλληλη ρύθμιση του __spec__ ισχύει εξίσου για modules που αρχικοποιούνται κατά την εκκίνηση του διερμηνέα. Η μόνη εξαίρεση είναι το __main__, όπου το __spec__ τίθεται σε None σε ορισμένες περιπτώσεις.
Δείτε την ModuleSpec για λεπτομέρειες σχετικά με τα περιεχόμενα του module spec.
Added in version 3.4.
5.4.4. Χαρακτηριστικά __path__ στα modules¶
Το χαρακτηριστικό __path__ θα πρέπει να είναι μια (ενδεχομένως κενή) sequence από συμβολοσειρές που απαριθμούν τις τοποθεσίες όπου θα βρεθούν τα submodules του πακέτου. Εξ ορισμού, αν ένα module έχει χαρακτηριστικό __path__, τότε είναι package.
Το χαρακτηριστικό __path__ ενός πακέτου χρησιμοποιείται κατά την εισαγωγή των subpackages του. Μέσα στον μηχανισμό εισαγωγής, λειτουργεί παρόμοια με το sys.path, δηλαδή παρέχει μια λίστα τοποθεσιών στις οποίες αναζητούνται modules κατά την εισαγωγή. Ωστόσο, το __path__ είναι συνήθως πολύ πιο περιορισμένο από το sys.path.
Οι ίδιοι κανόνες που ισχύουν για το sys.path ισχύουν επίσης και για το __path__ ενός πακέτου. Τα sys.path_hooks (που περιγράφονται παρακάτω) λαμβάνονται υπόψη κατά τη διάσχιση του __path__ ενός πακέτου.
Το αρχείο __init__.py ενός πακέτου μπορεί να ορίζει ή να τροποποιεί το χαρακτηριστικό __path__ του πακέτου, και αυτός ήταν συνήθως ο τρόπος με τον οποίο υλοποιούνταν τα namespace πακέτα πριν το PEP 420. Με την υιοθέτηση του PEP 420, τα namespace πακέτα δεν χρειάζεται πλέον να παρέχουν αρχεία __init__.py που περιέχουν μόνο κώδικα χειρισμού του __path__· ο μηχανισμός εισαγωγής ορίζει αυτόματα σωστά το __path__ για το namespace πακέτο.
5.4.5. Reprs των modules¶
Από προεπιλογή, όλα τα modules έχουν ένα χρησιμοποιήσιμο repr· ωστόσο, ανάλογα με τα χαρακτηριστικά που ορίζονται παραπάνω και στο spec του module, μπορείτε να ελέγχετε πιο ρητά το repr των αντικειμένων module.
Αν το module έχει spec (__spec__), ο μηχανισμός εισαγωγής θα προσπαθήσει να παράγει repr από αυτό. Αν αυτό αποτύχει ή δεν υπάρχει spec, το σύστημα εισαγωγής θα δημιουργήσει ένα προεπιλεγμένο repr χρησιμοποιώντας όποια πληροφορία είναι διαθέσιμη στο module. Θα προσπαθήσει να χρησιμοποιήσει τα module.__name__, module.__file__ και module.__loader__ ως είσοδο για το repr, με προεπιλογές για οποιαδήποτε πληροφορία λείπει.
Ακολουθούν οι ακριβείς κανόνες που χρησιμοποιούνται:
Αν το module έχει χαρακτηριστικό
__spec__, οι πληροφορίες στο spec χρησιμοποιούνται για να παραχθεί το repr. Λαμβάνονται υπόψη τα χαρακτηριστικά «name», «loader», «origin» και «has_location».Αν το module έχει χαρακτηριστικό
__file__, αυτό χρησιμοποιείται ως μέρος του repr του module.Αν το module δεν έχει
__file__αλλά έχει__loader__που δεν είναιNone, τότε το repr του loader χρησιμοποιείται ως μέρος του repr του module.Διαφορετικά, χρησιμοποιείται απλώς το
__name__του module στο repr.
Άλλαξε στην έκδοση 3.12: Η χρήση της module_repr(), η οποία ήταν δηλωμένη ως απαρχαιωμένη από την Python 3.4, αφαιρέθηκε στην Python 3.12 και δεν καλείται πλέον κατά την επίλυση του repr ενός module.
5.4.6. Ακύρωση cached bytecode¶
Πριν η Python φορτώσει cached bytecode από ένα αρχείο .pyc, ελέγχει αν η προσωρινή μνήμη είναι ενημερωμένη σε σχέση με το πηγαίο αρχείο .py. Από προεπιλογή, η Python το κάνει αυτό αποθηκεύοντας τη χρονική σήμανση της τελευταίας τροποποίησης και το μέγεθος του πηγαίου αρχείου στο αρχείο προσωρινής μνήμης κατά την εγγραφή του. Κατά το χρόνο εκτέλεσης, το σύστημα εισαγωγής επικυρώνει το αρχείο προσωρινής μνήμης ελέγχοντας τα αποθηκευμένα μεταδεδομένα του αρχείου προσωρινής μνήμης σε σχέση με τα μεταδεδομένα του πηγαίου αρχείου.
Η Python υποστηρίζει επίσης αρχεία προσωρινής μνήμης βασισμένα σε hash, τα οποία αποθηκεύουν ένα hash του περιεχομένου του πηγαίου αρχείου αντί για τα μεταδεδομένα του. Υπάρχουν δύο παραλλαγές των .pyc αρχείων βασισμένων σε hash: checked και unchecked. Για τα checked .pyc αρχεία που βασίζονται σε hash, η Python επικυρώνει το αρχείο προσωρινής μνήμης δημιουργώντας hash του πηγαίου αρχείου και συγκρίνοντας το αποτέλεσμα με το hash στο αρχείο προσωρινής μνήμης. Αν ένα checked αρχείο προσωρινής μνήμης βασισμένο σε hash βρεθεί μη έγκυρο, η Python το αναδημιουργεί και γράφει ένα νέο checked αρχείο προσωρινής μνήμης βασισμένο σε hash. Για τα unchecked .pyc αρχεία βασισμένα σε hash, η Python απλώς θεωρεί ότι το αρχείο προσωρινής μνήμης είναι έγκυρο αν υπάρχει. Η συμπεριφορά επικύρωσης των .pyc αρχείων βασισμένων σε hash μπορεί να υπερκαλυφθεί με τη σημαία --check-hash-based-pycs.
Άλλαξε στην έκδοση 3.7: Προστέθηκαν``.pyc`` αρχεία βασισμένα σε hash. Προηγουμένως, η Python υποστήριζε μόνο ακύρωση των bytecode caches με βάση χρονικές σημάνσεις.
5.5. Finder βασισμένος στη διαδρομή¶
Όπως αναφέρθηκε προηγουμένως, η Python συνοδεύεται από αρκετούς προεπιλεγμένους meta path finders. Ένας από αυτούς, που ονομάζεται path based finder (PathFinder), αναζητά σε ένα import path, το οποίο περιέχει μια λίστα από path entries. Κάθε path entry ονομάζει μια τοποθεσία όπου θα αναζητηθούν modules.
Ο ίδιος ο path based finder δεν «γνωρίζει» πώς να εισάγει οτιδήποτε. Αντ’ αυτού, διασχίζει τις επιμέρους καταχωρήσεις διαδρομής, συσχετίζοντας το καθένα με έναν finder καταχώρησης διαδρομής που γνωρίζει πώς να χειριστεί αυτόν τον συγκεκριμένο τύπο διαδρομής.
Το προεπιλεγμένο σύνολο finders καταχώρησης διαδρομής υλοποιεί όλη τη σημασιολογία για την εύρεση των modules στο σύστημα αρχείων, χειριζόμενο ειδικούς τύπους αρχείων όπως πηγαίο κώδικα Python (αρχεία .py), byte code Python (αρχεία .pyc) και κοινόχρηστες βιβλιοθήκες (π.χ. αρχεία .so). Όταν υποστηρίζεται από το module zipimport της τυπικής βιβλιοθήκης, οι προεπιλεγμένοι finders καταχώρησης διαδρομής χειρίζονται επίσης τη φόρτωση όλων αυτών των τύπων αρχείων (εκτός από κοινόχρηστες βιβλιοθήκες) από zipfiles.
Οι καταχωρήσεις διαδρομής δεν χρειάζεται να περιορίζονται σε τοποθεσίες του συστήματος αρχείων. Μπορούν να αναφέρονται σε URLs, σε ερωτήματα βάσεων δεδομένων ή σε οποιαδήποτε άλλη τοποθεσία που μπορεί να εκφραστεί ως συμβολοσειρά.
Ο path based finder παρέχει επιπλέον hooks και πρωτόκολλα ώστε να μπορείτε να επεκτείνετε και να προσαρμόσετε τους τύπους των καταχωρήσεων διαδρομής που μπορούν να αναζητηθούν. Για παράδειγμα, αν θέλατε να υποστηρίξετε καταχωρήσεις διαδρομής ως δικτυακά URLs, θα μπορούσατε να γράψετε ένα hook που υλοποιεί HTTP σημασιολογία για να βρίσκει modules στο διαδίκτυο. Αυτό το hook (ένα callable) θα επέστρεφε έναν path entry finder που υποστηρίζει το πρωτόκολλο που περιγράφεται παρακάτω, και στη συνέχεια θα χρησιμοποιούνταν για να ληφθεί ένας loader για το module από το διαδίκτυο.
Μια προειδοποίηση: αυτή η ενότητα και η προηγούμενη χρησιμοποιούν και οι δύο τον όρο finder, διακρίνοντάς τους με τους όρους meta path finder και path entry finder. Αυτοί οι δύο τύποι finders είναι πολύ παρόμοιοι, υποστηρίζουν παρόμοια πρωτόκολλα και λειτουργούν με παρόμοιο τρόπο κατά τη διαδικασία εισαγωγής, αλλά είναι σημαντικό να θυμάστε ότι έχουν λεπτές διαφορές. Συγκεκριμένα, οι meta path finders λειτουργούν στην αρχή της διαδικασίας εισαγωγής, όπως καθορίζεται από τη διάσχιση του sys.meta_path.
Αντίθετα, οι path entry finders είναι κατά κάποιον τρόπο μια λεπτομέρεια υλοποίησης του path based finder και, στην πραγματικότητα, αν ο path based finder αφαιρούνταν από το sys.meta_path, δεν θα ενεργοποιούνταν καθόλου η σημασιολογία των finders καταχώρησης διαδρομής.
5.5.1. Finders καταχώρησης διαδρομής (Path entry finders)¶
Ο path based finder είναι υπεύθυνος για την εύρεση και φόρτωση Python modules και πακέτων των οποίων η τοποθεσία καθορίζεται με μία συμβολοσειρά path entry. Οι περισσότερες καταχωρήσεις διαδρομής (path entries) δηλώνουν τοποθεσίες στο σύστημα αρχείων, αλλά δεν περιορίζονται απαραίτητα σε αυτό.
Ως ένα meta path finder, ο path based finder υλοποιεί το πρωτόκολλο find_spec() που περιγράφηκε προηγουμένως, ωστόσο εκθέτει επιπλέον hooks που μπορούν να χρησιμοποιηθούν για να προσαρμόσετε το πώς βρίσκονται και φορτώνονται τα modules από το import path.
Τρεις μεταβλητές χρησιμοποιούνται από τον path based finder: sys.path, sys.path_hooks και sys.path_importer_cache. Επίσης χρησιμοποιούνται τα χαρακτηριστικά __path__ σε αντικείμενα πακέτου. Αυτά παρέχουν επιπλέον τρόπους παραμετροποίησης του μηχανισμού εισαγωγής.
Το sys.path περιέχει μια λίστα από συμβολοσειρές που παρέχουν τοποθεσίες αναζήτησης για modules και πακέτα. Αρχικοποιείται από τη μεταβλητή περιβάλλοντος PYTHONPATH και από διάφορες άλλες προεπιλογές που εξαρτώνται από την εγκατάσταση και την υλοποίηση. Οι εγγραφές στο sys.path μπορεί να είναι φάκελοι στο σύστημα αρχείων, zip αρχεία και ενδεχομένως άλλες «τοποθεσίες» (βλ. το module site) στις οποίες πρέπει να γίνει αναζήτηση για modules, όπως URLs ή ερωτήματα βάσεων δεδομένων. Στο sys.path θα πρέπει να υπάρχουν μόνο συμβολοσειρές· όλοι οι άλλοι τύποι δεδομένων αγνοούνται.
Ο path based finder είναι meta path finder, επομένως ο μηχανισμός εισαγωγής ξεκινά την αναζήτηση στο import path καλώντας τη μέθοδο find_spec() του path based finder, όπως περιγράφηκε προηγουμένως. Όταν δοθεί το όρισμα path στη find_spec(), αυτό θα είναι μια λίστα από συμβολοσειρές διαδρομής προς διάσχιση - συνήθως το χαρακτηριστικό __path__ ενός πακέτου για εισαγωγή μέσα σε εκείνο το πακέτο. Αν το όρισμα path είναι None, αυτό υποδηλώνει εισαγωγή ανωτέρου επιπέδου και χρησιμοποιείται το sys.path.
Ο path based finder επαναλαμβάνεται πάνω σε κάθε εγγραφή στη διαδρομή αναζήτησης και, για καθεμία, αναζητά έναν κατάλληλο path entry finder (PathEntryFinder) για τη συγκεκριμένη καταχώρηση διαδρομής. Επειδή αυτό μπορεί να είναι δαπανηρή λειτουργία (π.χ. μπορεί να υπάρχει overhead από κλήσεις stat() για αυτή την αναζήτηση), ο path based finder διατηρεί μια προσωρινή μνήμη που αντιστοιχίζει καταχωρήσεις διαδρομής σε path entry finders. Αυτή η προσωρινή μνήμη διατηρείται στο sys.path_importer_cache (παρά το όνομα, αυτή η προσωρινή μνήμη αποθηκεύει αντικείμενα finder και όχι μόνο importer αντικείμενα). Με αυτόν τον τρόπο, η ακριβή αναζήτηση για τον path entry μιας συγκεκριμένης τοποθεσίας path entry finder χρειάζεται να γίνει μόνο μία φορά. Ο κώδικας του χρήστη μπορεί να αφαιρεί εγγραφές από το sys.path_importer_cache, αναγκάζοντας τον path based finder να εκτελέσει ξανά την αναζήτηση.
Αν η καταχώρηση διαδρομής δεν υπάρχει στην προσωρινή μνήμη, ο path based finder επαναλαμβάνεται πάνω σε κάθε callable στο sys.path_hooks. Κάθε path entry hook σε αυτή τη λίστα καλείται με ένα μόνο όρισμα: την καταχώρηση διαδρομής που θα αναζητηθεί. Αυτό το callable μπορεί είτε να επιστρέψει έναν path entry finder που μπορεί να χειριστεί την καταχώρηση διαδρομής, είτε να κάνει raise ένα ImportError. Το ImportError χρησιμοποιείται από τον path based finder για να δηλώσει ότι το hook δεν μπορεί να βρει path entry finder για το συγκεκριμένο path entry. Η εξαίρεση αγνοείται και η επανάληψη στο import path συνεχίζεται. Το hook θα πρέπει να αναμένει είτε συμβολοσειρά είτε αντικείμενο bytes· η κωδικοποίηση των bytes αντικειμένων επαφίεται στο hook (π.χ. μπορεί να είναι μία κωδικοποίηση του συστήματος αρχείων, UTF-8 ή κάτι άλλο), και αν το hook δεν μπορεί να κάνει αποκωδικοποίηση το όρισμα, θα πρέπει να κάνει raise ένα ImportError.
Αν η επανάληψη στο sys.path_hooks ολοκληρωθεί χωρίς να επιστραφεί path entry finder, τότε η μέθοδος find_spec() του path based finder θα αποθηκεύσει None στο sys.path_importer_cache (για να δηλώσει ότι δεν υπάρχει finder για αυτή την καταχώρηση διαδρομής) και θα επιστρέψει None, δηλώνοντας ότι αυτός ο meta path finder δεν μπόρεσε να βρει το module.
Αν επιστραφεί ένας path entry finder από κάποιο από τα path entry hook callables στο sys.path_hooks, τότε χρησιμοποιείται το ακόλουθο πρωτόκολλο για να ζητηθεί από τον finder ένα module spec, το οποίο στη συνέχεια χρησιμοποιείται κατά τη φόρτωση του module.
Ο τρέχων κατάλογος εργασίας – που δηλώνεται με κενή συμβολοσειρά – αντιμετωπίζεται λίγο διαφορετικά από τις άλλες εγγραφές στο sys.path. Πρώτον, αν δεν μπορεί να προσδιοριστεί ή αν διαπιστωθεί ότι δεν υπάρχει, δεν αποθηκεύεται τιμή στο sys.path_importer_cache. Δεύτερον, η τιμή για τον τρέχοντα κατάλογο εργασίας αναζητείται εκ νέου για κάθε αναζήτηση module. Τρίτον, η διαδρομή που χρησιμοποιείται για το sys.path_importer_cache και επιστρέφεται από τη importlib.machinery.PathFinder.find_spec() θα είναι ο πραγματικός τρέχων κατάλογος εργασίας και όχι η κενή συμβολοσειρά.
5.5.2. Πρωτόκολλο path entry finder¶
Για να υποστηρίζεται η εισαγωγή των modules και των αρχικοποιημένων πακέτων, καθώς και για τη συνεισφορά portions σε namespace πακέτα, οι path entry finders πρέπει να υλοποιούν τη μέθοδο find_spec().
Η find_spec() παίρνει δύο ορίσματα: το πλήρως αναγνωρισμένο όνομα του module που εισάγεται και το (προαιρετικό) target module. Η find_spec() επιστρέφει ένα πλήρως συμπληρωμένο spec για το module. Αυτό το spec θα έχει πάντα ορισμένο το «loader» (με μία εξαίρεση).
Για να δηλώσει στον μηχανισμό εισαγωγής ότι το spec αναπαριστά ένα namespace portion, ο path entry finder ορίζει το submodule_search_locations σε μια λίστα που περιέχει το portion.
Άλλαξε στην έκδοση 3.4: Η find_spec() αντικατέστησε τις find_loader() και find_module(), οι οποίες έχουν πλέον χαρακτηριστεί ως απαρχαιωμένες, αλλά θα χρησιμοποιηθούν αν δεν ορίζεται η find_spec().
Παλαιότεροι path entry finders μπορεί να υλοποιούν μία από αυτές τις δύο απαρχαιωμένες μεθόδους αντί για την find_spec(). Οι μέθοδοι αυτές εξακολουθούν να γίνονται δεκτές για λόγους συμβατότητας προς τα πίσω. Ωστόσο, αν ο path entry finder υλοποιεί find_spec(), οι παλιές μέθοδοι αγνοούνται.
Η find_loader() παίρνει ένα όρισμα, το πλήρως αναγνωρισμένο όνομα του module που εισάγεται. Η find_loader() επιστρέφει ένα tuple όπου το πρώτο στοιχείο είναι ο loader και το δεύτερο στοιχείο είναι ένα namespace portion.
Για συμβατότητα προς τα πίσω με άλλες υλοποιήσεις του πρωτοκόλλου εισαγωγής, πολλοί path entry finders υποστηρίζουν επίσης την ίδια παραδοσιακή μέθοδο find_module() που υποστηρίζουν και οι meta path finders. Ωστόσο, οι μέθοδοι find_module() των path entry finders δεν καλούνται ποτέ με όρισμα path (αναμένεται να έχουν καταγράψει την κατάλληλη πληροφορία διαδρομής από την αρχική κλήση του path hook).
Η μέθοδος find_module() στους path entry finders έχει χαρακτηριστεί ως απαρχαιωμένη, καθώς δεν επιτρέπει στον path entry finder να συνεισφέρει portions σε namespace πακέτα. Αν ένας path entry finder διαθέτει και find_loader() και find_module(), το σύστημα εισαγωγής θα καλεί πάντα την find_loader() αντί για την find_module().
Άλλαξε στην έκδοση 3.10: Οι κλήσεις των find_module() και find_loader() από το σύστημα εισαγωγής θα κάνουν raise μία ImportWarning.
Άλλαξε στην έκδοση 3.12: Οι find_module() και find_loader() έχουν αφαιρεθεί.
5.6. Αντικατάσταση του τυπικού συστήματος εισαγωγής¶
Ο πιο αξιόπιστος μηχανισμός για την αντικατάσταση ολόκληρου του συστήματος εισαγωγής είναι η διαγραφή των προεπιλεγμένων περιεχομένων του sys.meta_path, αντικαθιστώντας τα πλήρως με ένα προσαρμοσμένο hook μετά-διαδρομής.
If it is acceptable to only alter the behaviour of import statements
without affecting other APIs that access the import system, then replacing
the builtin __import__() function may be sufficient.
Για να αποτρέψετε επιλεκτικά την εισαγωγή ορισμένων modules από ένα hook νωρίς στη μεταδιαδρομή (αντί να απενεργοποιήσετε πλήρως το τυπικό σύστημα εισαγωγής), αρκεί να κάνετε raise το ModuleNotFoundError απευθείας από τη find_spec() αντί να επιστρέψετε None. Το None δηλώνει ότι η αναζήτηση στη μεταδιαδρομή πρέπει να συνεχιστεί, ενώ η υποβολή αιτήματος εξαίρεσης την τερματίζει άμεσα.
5.7. Σχετικές εισαγωγές σε πακέτα¶
Οι σχετικές εισαγωγές χρησιμοποιούν τελείες στην αρχή. Μια και μόνο τελεία στην αρχή δηλώνει σχετική εισαγωγή που ξεκινά από το τρέχον πακέτο. Δύο ή περισσότερες τελείες δηλώνουν σχετική εισαγωγή προς το/τα γονικό/ά του τρέχοντος πακέτου, ένα επίπεδο ανά τελεία μετά την πρώτη. Για παράδειγμα, με την ακόλουθη διάταξη πακέτου:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
Είτε στο subpackage1/moduleX.py είτε στο subpackage1/__init__.py, τα παρακάτω είναι έγκυρες σχετικές εισαγωγές:
from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo
Οι απόλυτες εισαγωγές μπορούν να χρησιμοποιούν είτε τη σύνταξη import <> είτε τη from <> import <>, αλλά οι σχετικές εισαγωγές μπορούν να χρησιμοποιούν μόνο τη δεύτερη μορφή· ο λόγος είναι ότι:
import XXX.YYY.ZZZ
θα πρέπει να εκθέτει το XXX.YYY.ZZZ ως χρησιμοποιήσιμη έκφραση, αλλά το .moduleY δεν είναι έγκυρη έκφραση.
5.8. Ειδικές περιπτώσεις για το __main__¶
Το module __main__ είναι μια ειδική περίπτωση σε σχέση με το σύστημα εισαγωγής της Python. Όπως σημειώνεται αλλού, το __main__ αρχικοποιείται απευθείας κατά την εκκίνηση του διερμηνέα, όπως και τα sys και builtins. Ωστόσο, σε αντίθεση με αυτά τα δύο, δεν χαρακτηρίζεται αυστηρά ως ενσωματωμένο module. Αυτό συμβαίνει επειδή ο τρόπος με τον οποίο αρχικοποιείται το __main__ εξαρτάται από τις σημαίες και άλλες επιλογές με τις οποίες καλείται ο διερμηνέας.
5.8.1. __main__.__spec__¶
Ανάλογα με το πώς αρχικοποιείται το __main__, το __main__.__spec__ ορίζεται κατάλληλα ή σε None.
Όταν η Python ξεκινά με την επιλογή -m, το __spec__ ορίζεται στο module spec του αντίστοιχου module ή πακέτου. Το __spec__ συμπληρώνεται επίσης όταν το module __main__ φορτώνεται ως μέρος της εκτέλεσης ενός καταλόγου, zipfile ή άλλης εγγραφής του sys.path.
Στις υπόλοιπες περιπτώσεις το __main__.__spec__ ορίζεται σε None, καθώς ο κώδικας που χρησιμοποιείται για να γεμίσει το __main__ δεν αντιστοιχεί άμεσα σε εισαγόμενο module:
διαδραστική προτροπή
επιλογή
-cεκτέλεση από stdin
εκτέλεση απευθείας από το αρχείο πηγαίου κώδικα ή bytecode
Σημειώστε ότι στην τελευταία περίπτωση το __main__.__spec__ είναι πάντα None, ακόμη κι αν το αρχείο θα μπορούσε τεχνικά να εισαχθεί απευθείας ως module. Χρησιμοποιήστε το switch -m αν χρειάζεστε έγκυρα module μεταδεδομένων στο __main__.
Σημειώστε επίσης ότι ακόμη κι όταν το __main__ αντιστοιχεί σε εισαγόμενα module και το __main__.__spec__ ορίζεται ανάλογα, εξακολουθούν να θεωρούνται διακριτά modules. Αυτό οφείλεται στο ότι μπλοκ κώδικα που προστατεύονται από ελέγχους if __name__ == "__main__": εκτελούνται μόνο όταν το module χρησιμοποιείται για να γεμίσει το namespace του __main__, και όχι κατά την κανονική εισαγωγή.
5.9. Αναφορές¶
Ο μηχανισμός εισαγωγής έχει εξελιχθεί σημαντικά από τα πρώτα χρόνια της Python. Η αρχική προδιαγραφή για πακέτα είναι ακόμη διαθέσιμη προς ανάγνωση, αν και ορισμένες λεπτομέρειες έχουν αλλάξει από τότε που γράφτηκε αυτό το κείμενο.
Η αρχική προδιαγραφή για το sys.meta_path ήταν το PEP 302, με μεταγενέστερη επέκταση στο PEP 420.
Το PEP 420 εισήγαγε τα namespace πακέτα στην Python 3.3. Το PEP 420 εισήγαγε επίσης το πρωτόκολλο find_loader() ως εναλλακτική της find_module().
Το PEP 366 περιγράφει την προσθήκη του χαρακτηριστικού __package__ για ρητές σχετικές εισαγωγές σε κύρια modules.
Το PEP 328 εισήγαγε απόλυτες και ρητές σχετικές εισαγωγές και αρχικά πρότεινε το __name__ για τη σημασιολογία που το PEP 366 τελικά όρισε για το __package__.
Το PEP 338 ορίζει την εκτέλεση modules ως scripts.
Το PEP 451 προσθέτει την ενθυλάκωση της κατάστασης εισαγωγής ανά module σε spec αντικείμενα. Επίσης μεταφέρει το μεγαλύτερο μέρος των τυποποιημένων ευθυνών των loaders πίσω στον μηχανισμό εισαγωγής. Αυτές οι αλλαγές επιτρέπουν την απόσυρση αρκετών APIs στο σύστημα εισαγωγής και επίσης την προσθήκη νέων μεθόδων σε finders και loaders.
Υποσημειώσεις