# 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

    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: The import system has taken over the boilerplate responsibilities of loaders. These were previously performed by the `importlib.abc.Loader.load_module` method.

Άλλαξε στην έκδοση 3.15: The `load_module` method is no longer used.

### 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: The `importlib.abc.Loader.load_module` method was replaced by `exec_module()` and the import machinery assumed all the boilerplate responsibilities of loading.

Για συμβατότητα με υπάρχοντες 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.

Υποσημειώσεις

[1]

Δείτε την `types.ModuleType`.

[2]

Η υλοποίηση του importlib αποφεύγει να χρησιμοποιεί απευθείας την τιμή επιστροφής. Αντ’ αυτού, λαμβάνει το αντικείμενο module αναζητώντας το όνομα του module στο `sys.modules`. Έμμεση συνέπεια αυτού είναι ότι ένα εισηγμένο module μπορεί να αντικαταστήσει τον εαυτό του στο `sys.modules`. Αυτή είναι μία ειδική συμπεριφορά της συγκεκριμένης υλοποίησης και δεν είναι εγγυημένο ότι λειτουργεί σε άλλες υλοποιήσεις της Python.