annotationlib — Λειτουργικότητα για την ενδοσκόπηση των επισημάνσεων (annotations)¶
Added in version 3.14.
Πηγαίος κώδικας: Lib/annotationlib.py
Το module annotationlib παρέχει εργαλεία για την ενδοσκόπηση των annotations σε modules, κλάσεις και συναρτήσεις.
Οι επισημάνσεις (annotations) αξιολογούνται νωχελικά και συχνά περιέχουν αναφορές προς τα εμπρός σε αντικείμενα που δεν έχουν ακόμη οριστεί όταν δημιουργείται η επισήμανση (annotation). Αυτό το module παρέχει ένα σύνολο εργαλείων χαμηλού επιπέδου, που μπορούν να χρησιμοποιηθούν για την αξιόπιστη ανάκτηση των επισημάνσεων (annotations), ακόμη και στην παρουσία αναφορών προς τα εμπρός και άλλων ακραίων περιπτώσεων.
Αυτό το module υποστηρίζει την ανάκτηση των επισημάνσεων (annotations) σε τρεις κύριες μορφές (δείτε το Format), κάθε μία από τις οποίες λειτουργεί καλύτερα για διαφορετικές περιπτώσεις χρήσης:
Το
VALUEαξιολογεί τις επισημάνσεις (annotations) και επιστρέφει την τιμή τους. Αυτή είναι η πιο απλή μορφή για να εργαστεί κανείς, αλλά μπορεί να εγείρει σφάλματα, για παράδειγμα αν οι επισημάνσεις (annotations) περιέχουν αναφορές σε ονόματα που δεν έχουν οριστεί.Το
FORWARDREFεπιστρέφει αντικείμεναForwardRefγια επισημάνσεις (annotations) που δεν μπορούν να επιλυθούν, επιτρέποντάς σας να επιθεωρήσετε τις επισημάνσεις (annotations) χωρίς να τα αξιολογήσετε. Αυτό είναι χρήσιμο όταν χρειάζεται να εργαστείτε με επισημάνσεις (annotations) που μπορεί να περιέχουν ανεπίλυτες αναφορές προς τα εμπρός.Το
STRINGεπιστρέφει τις επισημάνσεις (annotations) ως συμβολοσειρά, ομοίως με τον τρόπο που θα εμφανίζονταν στο αρχείο του πηγαίου κώδικα. Αυτό είναι χρήσιμο για γεννήτριες τεκμηρίωσης που θέλουν να εμφανίζουν τις επισημάνσεις (annotations) με αναγνώσιμο τρόπο.
Η συνάρτηση get_annotations() είναι το κύριο σημείο εισόδου για την ανάκτηση των επισημάνσεων (annotations). Δοθείσας μιας συνάρτησης, μιας κλάσης ή ενός module, επιστρέφει ένα λεξικό των επισημάνσεων (annotations) στη ζητούμενη μορφή. Αυτό το module παρέχει επίσης τη λειτουργικότητα για την άμεση εργασία με την annotate function, που χρησιμοποιείται για την αξιολόγηση των επισημάνσεων (annotations), όπως οι get_annotate_from_class_namespace() και call_annotate_function(), καθώς και τη συνάρτηση call_evaluate_function() για εργασία με evaluate functions.
Προσοχή
Η περισσότερη λειτουργικότητα σε αυτό το module μπορεί να εκτελέσει αυθαίρετο κώδικα· δείτε την ενότητα ασφαλείας για περισσότερες πληροφορίες.
Δείτε επίσης
Το PEP 649 πρότεινε το τρέχον μοντέλο για το πώς λειτουργούν οι επισημάνσεις (annotations) στην Python.
Το PEP 749 επέκτεινε διάφορες πτυχές του PEP 649 και εισήγαγε το module annotationlib.
Το Annotations Best Practices παρέχει βέλτιστες πρακτικές για την εργασία με τις επισημάνσεις (annotations).
Το typing-extensions παρέχει ένα backport της get_annotations() που λειτουργεί σε προγενέστερες εκδόσεις της Python.
Σημασιολογία των επισημάνσεων (annotations)¶
Ο τρόπος αξιολόγησης των επισημάνσεων (annotations) έχει αλλάξει κατά τη διάρκεια της ιστορίας της Python 3 και επί του παρόντος εξαρτάται ακόμα από μια εισαγωγή future. Υπήρξαν μοντέλα εκτέλεσης για τις επισημάνσεις (annotations):
Βασική σημασιολογία (προεπιλογή στην Python από την έκδοση 3.0 έως την 3.13· δείτε τα PEP 3107 και PEP 526): οι επισημάνσεις (annotations) αξιολογούνται άμεσα, όπως συναντώνται στον πηγαίο κώδικα.
Επισημάνσεις (annotations) ως συμβολοσειρές (χρησιμοποιούνται με το
from __future__ import annotationsαπό την Python 3.7 και μετά· δείτε το PEP 563): οι επισημάνσεις (annotations) αποθηκεύονται μόνο ως συμβολοσειρές.Αναβαλλόμενη αξιολόγηση (προεπιλογή από την Python 3.14 και μετά· δείτε τα PEP 649 και PEP 749): οι επισημάνσεις (annotations) αξιολογούνται νωχελικά, μόνο όταν προσπελαύνονται.
Ως παράδειγμα, εξετάστε το ακόλουθο πρόγραμμα:
def func(a: Cls) -> None:
print(a)
class Cls: pass
print(func.__annotations__)
Αυτό θα συμπεριφερθεί ως εξής:
Υπό τη βασική σημασιολογία (Python 3.13 και προγενέστερες), θα εγείρει ένα
NameErrorστη γραμμή όπου ορίζεται ηfunc, επειδή τοClsείναι ένα μη ορισμένο όνομα σε εκείνο το σημείο.Υπό τις επισημάνσεις (annotations) ως συμβολοσειρές (αν χρησιμοποιείται το
from __future__ import annotations), θα εκτυπώσει{'a': 'Cls', 'return': 'None'}.Υπό την αναβαλλόμενη αξιολόγηση (Python 3.14 και μεταγενέστερες), θα εκτυπώσει
{'a': <class 'Cls'>, 'return': None}.
Η βασική σημασιολογία χρησιμοποιήθηκε όταν οι επισημάνσεις (annotations) συναρτήσεων εισήχθησαν για πρώτη φορά στην Python 3.0 (από το PEP 3107), επειδή ήταν ο απλούστερος, πιο προφανής τρόπος υλοποίησης των επισημάνσεων (annotations). Το ίδιο μοντέλο εκτέλεσης χρησιμοποιήθηκε όταν εισήχθησαν οι επισημάνσεις (annotations) μεταβλητών στην Python 3.6 (από το PEP 526). Ωστόσο, η βασική σημασιολογία προκάλεσε προβλήματα κατά τη χρήση των επισημάνσεων (annotations) ως υποδείξεων τύπων, όπως η ανάγκη αναφοράς σε ονόματα που δεν έχουν ακόμη οριστεί όταν συναντάται η επισήμανση. Επιπλέον, υπήρξαν προβλήματα απόδοσης με την εκτέλεση των επισημάνσεων (annotations) κατά τον χρόνο εισαγωγής του module. Επομένως, στην Python 3.7, το PEP 563 εισήγαγε τη δυνατότητα αποθήκευσης των επισημάνσεων (annotations) ως συμβολοσειρές χρησιμοποιώντας τη σύνταξη from __future__ import annotations. Το σχέδιο τότε ήταν να γίνει τελικά αυτή η συμπεριφορά η προεπιλογή, αλλά εμφανίστηκε ένα πρόβλημα: οι επισημάνσεις (annotations) ως συμβολοσειρές είναι πιο δύσκολο να επεξεργαστούν για όσα άτομα κάνουν ενδοσκόπηση στις επισημάνσεις (annotations) κατά τον χρόνο εκτέλεσης. Σαν εναλλακτική πρόταση, το PEP 649, εισήγαγε το τρίτο μοντέλο εκτέλεσης, την αναβαλλόμενη αξιολόγηση, και υλοποιήθηκε στην Python 3.14. Οι επισημάνσεις (annotations) ως συμβολοσειρές εξακολουθούν να χρησιμοποιούνται αν υπάρχει το from __future__ import annotations, αλλά αυτή η συμπεριφορά θα αφαιρεθεί τελικά.
Κλάσεις¶
- class annotationlib.Format¶
Μία
IntEnumπου περιγράφει τις μορφές στις οποίες μπορούν να επιστραφούν οι επισημάνσεις (annotations). Τα μέλη του enum, ή οι ισοδύναμες ακέραιες τιμές τους, μπορούν να περαστούν στηget_annotations()και σε άλλες συναρτήσεις αυτού του module, καθώς και σε συναρτήσεις__annotate__.- VALUE = 1¶
Οι τιμές είναι το αποτέλεσμα της αξιολόγησης των εκφράσεων σχολιασμού.
- VALUE_WITH_FAKE_GLOBALS = 2¶
Ειδική τιμή που χρησιμοποιείται για να σηματοδοτήσει ότι μια συνάρτηση σχολιασμού αξιολογείται σε ένα ειδικό περιβάλλον με ψεύτικα καθολικά (fake globals). Όταν περνιέται αυτή η τιμή, οι συναρτήσεις σχολιασμού θα πρέπει είτε να επιστρέψουν την ίδια τιμή όπως για τη μορφή
Format.VALUE, είτε να εγείρουνNotImplementedErrorγια να σηματοδοτήσουν ότι δεν υποστηρίζουν εκτέλεση σε αυτό το περιβάλλον. Αυτή η μορφή χρησιμοποιείται μόνο εσωτερικά και δεν πρέπει να περνιέται στις συναρτήσεις αυτού του module.
- FORWARDREF = 3¶
Οι τιμές είναι πραγματικές τιμές σχολιασμού (όπως στη μορφή
Format.VALUE) για ορισμένες τιμές, και αντιπρόσωποι (proxies)ForwardRefγια μη ορισμένες τιμές. Τα πραγματικά αντικείμενα μπορεί να περιέχουν αναφορές σε αντικείμενα αντιπροσώπωνForwardRef.
- STRING = 4¶
Οι τιμές είναι η συμβολοσειρά κειμένου της επισήμανσης (annotation) όπως εμφανίζεται στον πηγαίο κώδικα, με κάποιες τροποποιήσεις που περιλαμβάνουν κανονικοποιήσεις λευκών χαρακτήρων και βελτιστοποιήσεις σταθερών τιμών (αλλά δεν περιορίζονται σε αυτές).
Οι ακριβείς τιμές αυτών των συμβολοσειρών ενδέχεται να αλλάξουν σε μελλοντικές εκδόσεις της Python.
Added in version 3.14.
- class annotationlib.ForwardRef¶
Ένα αντικείμενο αντιπροσώπου για αναφορές προς τα εμπρός σε επισημάνσεις (annotations).
Στιγμιότυπα αυτής της κλάσης επιστρέφονται όταν χρησιμοποιείται η μορφή
FORWARDREFκαι οι επισημάνσεις (annotations) περιέχουν ένα όνομα που δεν μπορεί να επιλυθεί. Αυτό μπορεί να συμβεί όταν χρησιμοποιείται μια αναφορά προς τα εμπρός σε μια επισήμανση (annotation), όπως όταν γίνεται αναφορά σε μια κλάση πριν αυτή οριστεί.- __forward_arg__¶
Μια συμβολοσειρά που περιέχει τον κώδικα που αξιολογήθηκε για να παραχθεί η
ForwardRef. Η συμβολοσειρά μπορεί να μην είναι ακριβώς ισοδύναμη με τον αρχικό πηγαίο κώδικα.
- evaluate(*, owner=None, globals=None, locals=None, type_params=None, format=Format.VALUE)¶
Αξιολογεί την αναφορά προς τα εμπρός, επιστρέφοντας την τιμή της.
Αν το όρισμα format είναι
VALUE(η προεπιλογή), αυτή η μέθοδος μπορεί να προκαλέσει μια εξαίρεση, όπως τηνNameError, αν η αναφορά προς τα εμπρός αναφέρεται σε ένα όνομα που δεν μπορεί να επιλυθεί. Τα ορίσματα αυτής της μεθόδου μπορούν να χρησιμοποιηθούν για να παρέχουν συνδέσεις για ονόματα που διαφορετικά θα ήταν μη ορισμένα. Αν το όρισμα format είναιFORWARDREF, η μέθοδος δεν θα προκαλέσει ποτέ εξαίρεση, αλλά μπορεί να επιστρέψει ένα στιγμιότυπο τηςForwardRef. Για παράδειγμα, αν το αντικείμενο αναφοράς προς τα εμπρός περιέχει τον κώδικαlist[undefined], όπουundefinedείναι ένα όνομα που δεν έχει οριστεί, η αξιολόγησή του με τη μορφήFORWARDREFθα επιστρέψειlist[ForwardRef('undefined')]. Αν το όρισμα format είναιSTRING, η μέθοδος θα επιστρέψει__forward_arg__.Η παράμετρος owner παρέχει τον προτιμώμενο μηχανισμό για το πέρασμα πληροφοριών εμβέλειας σε αυτή τη μέθοδο. Ο ιδιοκτήτης μιας
ForwardRefείναι το αντικείμενο που περιέχει τον σχολιασμό από τον οποίο προέρχεται ηForwardRef, όπως ένα αντικείμενο module, αντικείμενο τύπου ή αντικείμενο συνάρτησης.Οι παράμετροι globals, locals και type_params παρέχουν έναν πιο ακριβή μηχανισμό για τον επηρεασμό των ονομάτων που είναι διαθέσιμα όταν αξιολογείται η
ForwardRef. Τα globals και locals περνιούνται στηeval(), αντιπροσωπεύοντας τους καθολικούς και τοπικούς χώρους ονομάτων στους οποίους αξιολογείται το όνομα. Η παράμετρος type_params είναι σχετική για αντικείμενα που δημιουργούνται χρησιμοποιώντας την εγγενή σύνταξη για γενικευμένες κλάσεις και συναρτήσεις. Είναι ένα tuple από παραμέτρους τύπου που είναι εντός εμβέλειας όσο η αναφορά προς τα εμπρός αξιολογείται. Για παράδειγμα, αν αξιολογείται μίαForwardRefπου ανακτήθηκε από έναν μια επισήμανση (annotation), που βρίσκεται στον χώρο ονομάτων κλάσης μιας γενικευμένης κλάσηςC, το type_params θα πρέπει να οριστεί ωςC.__type_params__.Τα στιγμιότυπα
ForwardRefπου επιστρέφονται από τηget_annotations()διατηρούν αναφορές σε πληροφορίες σχετικές με την εμβέλεια από την οποία προήλθαν, οπότε η κλήση αυτής της μεθόδου χωρίς περαιτέρω ορίσματα μπορεί να είναι επαρκής για την αξιολόγηση τέτοιων αντικειμένων. Τα στιγμιότυπα τηςForwardRefπου δημιουργούνται με άλλα μέσα μπορεί να μην έχουν καμία πληροφορία σχετικά με την εμβέλειά τους, οπότε το πέρασμα ορισμάτων σε αυτή τη μέθοδο μπορεί να είναι απαραίτητο για την επιτυχή αξιολόγησή τους.Αν δεν παρέχεται κανένα από τα owner, globals, locals ή type_params και η
ForwardRefδεν περιέχει πληροφορίες σχετικά με την προέλευσή της, χρησιμοποιούνται κενά καθολικά και τοπικά λεξικά.
Added in version 3.14.
Συναρτήσεις¶
- annotationlib.annotations_to_string(annotations)¶
Μετατρέπει ένα λεξικό επισημάνσεων (annotations) που περιέχει τιμές χρόνου εκτέλεσης, σε ένα λεξικό που περιέχει μόνο συμβολοσειρές. Αν οι τιμές δεν είναι ήδη συμβολοσειρές, μετατρέπονται χρησιμοποιώντας τη
type_repr(). Αυτό προορίζεται ως βοηθητικό εργαλείο για συναρτήσεις annotate, που παρέχονται από τον χρήστη και υποστηρίζουν τη μορφήSTRINGαλλά δεν έχουν πρόσβαση στον κώδικα που δημιουργεί τις επισημάνσεις (annotations).Για παράδειγμα, χρησιμοποιείται για την υλοποίηση της
STRINGγια κλάσειςtyping.TypedDictπου δημιουργούνται μέσω της συναρτησιακής σύνταξης:>>> from typing import TypedDict >>> Movie = TypedDict("movie", {"name": str, "year": int}) >>> get_annotations(Movie, format=Format.STRING) {'name': 'str', 'year': 'int'}
Added in version 3.14.
- annotationlib.call_annotate_function(annotate, format, *, owner=None)¶
Καλεί τη συνάρτηση σχολιασμού annotate με το δοθέν format, ένα μέλος του enum
Format, και επιστρέφει το λεξικό των επισημάνσεων (annotations) που παράγεται από τη συνάρτηση.Αυτή η βοηθητική συνάρτηση απαιτείται, επειδή οι συναρτήσεις annotate που δημιουργούνται από τον μεταγλωττιστή για τις συναρτήσεις, τις κλάσεις και τα modules υποστηρίζουν μόνο τη μορφή
VALUE, όταν καλούνται απευθείας. Για την υποστήριξη άλλων μορφών, αυτή η συνάρτηση καλεί τη συνάρτηση annotate σε ένα ειδικό περιβάλλον που της επιτρέπει να παράγει επισημάνσεις (annotations) στις άλλες μορφές. Αυτό είναι ένα χρήσιμο δομικό στοιχείο κατά την υλοποίηση λειτουργικότητας, που χρειάζεται να αξιολογεί μερικώς τις επισημάνσεις (annotations) ενώ κατασκευάζεται μια κλάση.Το owner είναι το αντικείμενο που κατέχει τη συνάρτηση annotate, συνήθως μια συνάρτηση, κλάση ή module. Αν παρέχεται, χρησιμοποιείται στη μορφή
FORWARDREFγια να παράγει ένα αντικείμενο τηςForwardRefπου μεταφέρει περισσότερες πληροφορίες.Δείτε επίσης
Το PEP 649 περιέχει μια εξήγηση της τεχνικής υλοποίησης που χρησιμοποιείται από αυτή τη συνάρτηση.
Added in version 3.14.
- annotationlib.call_evaluate_function(evaluate, format, *, owner=None)¶
Καλεί τη συνάρτηση αξιολόγησης evaluate με ο δοθέν format, ένα μέλος του enum
Format, και επιστρέφει την τιμή που παράγεται από τη συνάρτηση. Είναι παρόμοιο με τηνcall_annotate_function(), αλλά η τελευταία επιστρέφει πάντα ένα λεξικό που αντιστοιχίζει συμβολοσειρές σε επισημάνσεις (annotations), ενώ αυτή η συνάρτηση επιστρέφει μια ενιαία τιμή.Προορίζεται για χρήση με τις συναρτήσεις αξιολόγησης που δημιουργούνται για νωχελικά αξιολογούμενα στοιχεία, που σχετίζονται με ψευδώνυμα τύπων και παραμέτρους τύπου:
typing.TypeAliasType.evaluate_value(), η τιμή των ψευδωνύμων τύπωνtyping.TypeVar.evaluate_bound(), το όριο των μεταβλητών τύπουtyping.TypeVar.evaluate_constraints(), οι περιορισμοί των μεταβλητών τύπουtyping.TypeVar.evaluate_default(), η προεπιλεγμένη τιμή των μεταβλητών τύπουtyping.ParamSpec.evaluate_default(), η προεπιλεγμένη τιμή των προδιαγραφών παραμέτρωνtyping.TypeVarTuple.evaluate_default(), η προεπιλεγμένη τιμή των μεταβλητών τύπου tuple
Το owner είναι το αντικείμενο που κατέχει τη συνάρτηση αξιολόγησης, όπως το αντικείμενο ψευδωνύμου τύπου ή μεταβλητής τύπου.
Το format μπορεί να χρησιμοποιηθεί για τον έλεγχο της μορφής στην οποία επιστρέφεται η τιμή:
>>> type Alias = undefined >>> call_evaluate_function(Alias.evaluate_value, Format.VALUE) Traceback (most recent call last): ... NameError: name 'undefined' is not defined >>> call_evaluate_function(Alias.evaluate_value, Format.FORWARDREF) ForwardRef('undefined') >>> call_evaluate_function(Alias.evaluate_value, Format.STRING) 'undefined'
Added in version 3.14.
- annotationlib.get_annotate_from_class_namespace(namespace)¶
Ανακτά τη συνάρτηση σχολιασμού από ένα λεξικό χώρου ονομάτων κλάσης namespace. Επιστρέφει
Noneαν ο χώρος ονομάτων δεν περιέχει συνάρτηση annotate. Eίναι κυρίως χρήσιμο πριν η κλάση δημιουργηθεί πλήρως (π.χ., σε μια μετάκληση)· μετά την ύπαρξη της κλάσης, η συνάρτηση σχολιασμού μπορεί να ανακτηθεί με τηνcls.__annotate__. Δείτε παρακάτω για ένα παράδειγμα χρήσης αυτής της συνάρτησης σε μια μετάκληση.Added in version 3.14.
- annotationlib.get_annotations(obj, *, globals=None, locals=None, eval_str=False, format=Format.VALUE)¶
Υπολογίζει το λεξικό των επισημάνσεων (annotations) για ένα αντικείμενο.
Το obj μπορεί να είναι μια καλέσιμη οντότητα, μία κλάση, ένα module ή άλλο αντικείμενο με χαρακτηριστικά
__annotate__ή__annotations__. Το πέρασμα οποιουδήποτε άλλου αντικειμένου κάνει raise τηνTypeError.Η παράμετρος format ελέγχει τη μορφή στην οποία επιστρέφονται οι επισημάνσεις (annotations) και πρέπει να είναι μέλος του enum
Formatή το ακέραιο ισοδύναμό του. Οι διάφορες μορφές λειτουργούν ως εξής:VALUE: Δοκιμάζεται πρώτα το
object.__annotations__· αν αυτό δεν υπάρχει, καλείται η συνάρτησηobject.__annotate__αν υπάρχει.FORWARDREF: Αν το
object.__annotations__υπάρχει και μπορεί να αξιολογηθεί επιτυχώς, χρησιμοποιείται· διαφορετικά, καλείται η συνάρτησηobject.__annotate__. Αν ούτε αυτή υπάρχει, δοκιμάζεται ξανά τοobject.__annotations__και κάθε σφάλμα από την πρόσβαση σε αυτό γίνεται raise ξανά.Όταν καλείται το
object.__annotate__, καλείται πρώτα με τοFORWARDREF. Αν αυτό δεν είναι υλοποιημένο, στη συνέχεια ελέγχει αν υποστηρίζεται τοVALUE_WITH_FAKE_GLOBALSκαι το χρησιμοποιεί στο περιβάλλον με τα ψεύτικα καθολικά. Αν καμία από αυτές τις μορφές δεν υποστηρίζεται, θα καταφύγει στη χρήση τουVALUE. Αν τοVALUEαποτύχει, το σφάλμα από αυτή την κλήση θα γίνει raise.
STRING: Αν υπάρχει το
object.__annotate__, καλείται πρώτο· διαφορετικά, χρησιμοποιείται τοobject.__annotations__και μετατρέπεται σε συμβολοσειρά χρησιμοποιώντας τηνannotations_to_string().Όταν καλείται το
object.__annotate__, καλείται πρώτα μεSTRING. Αν αυτό δεν είναι υλοποιημένο, στη συνέχεια ελέγχει αν υποστηρίζεται τοVALUE_WITH_FAKE_GLOBALSκαι το χρησιμοποιεί στο περιβάλλον με τα ψεύτικα καθολικά. Αν καμία από αυτές τις μορφές δεν υποστηρίζεται, θα καταφύγει στη χρήση τουVALUEμε το αποτέλεσμα να μετατρέπεται χρησιμοποιώντας τηνannotations_to_string(). Αν τοVALUEαποτύχει, το σφάλμα από αυτή την κλήση θα γίνει raise.
Επιστρέφει ένα λεξικό. Η
get_annotations()επιστρέφει ένα νέο λεξικό κάθε φορά που καλείται· αν κληθεί δύο φορές στο ίδιο αντικείμενο θα επιστρέψει δύο διαφορετικά αλλά ισοδύναμα λεξικά.Αυτή η συνάρτηση χειρίζεται αρκετές λεπτομέρειες για εσάς:
Αν το eval_str είναι αληθές, οι τιμές τύπου
strθα απομετατραπούν από συμβολοσειρές χρησιμοποιώντας τηeval(). Προορίζεται για χρήση με επισημάνσεις (annotations) ως συμβολοσειρές (from __future__ import annotations). Είναι λάθος να οριστεί το eval_str ως αληθές με μορφές διαφορετικές από τοFormat.VALUE.Αν το obj δεν έχει λεξικό επισημάνσεων (annotations), επιστρέφει ένα κενό λεξικό. (Οι συναρτήσεις και οι μέθοδοι έχουν πάντα λεξικό επισημάνσεων· οι κλάσεις, τα modules και άλλοι τύποι καλέσιμων μπορεί να μην έχουν.)
Αγνοεί τις κληρονομημένες επισημάνσεις (annotations) σε κλάσεις, καθώς και τις επισημάνσεις (annotations) σε μετακλάσεις. Αν μια κλάση δεν έχει το δικό της λεξικό επισημάνσεων (annotations), επιστρέφει ένα κενό λεξικό.
Όλες οι προσβάσεις σε μέλη αντικειμένων και τιμές λεξικών γίνονται χρησιμοποιώντας τις
getattr()καιdict.get()για λόγους ασφαλείας.
Το eval_str ελέγχει αν οι τιμές τύπου
strαντικαθίστανται ή όχι με το αποτέλεσμα της κλήσης τηςeval()σε αυτές τις τιμές:Αν το eval_str είναι αληθές, η
eval()καλείται σε τιμές τύπουstr. (Σημειώστε ότι ηget_annotations()δεν πιάνει εξαιρέσεις· αν ηeval()κάνει raise μια εξαίρεση, θα ξετυλίξει τη στοίβα πέρα από την κλήση τηςget_annotations().)Αν το eval_str είναι ψευδές (η προεπιλογή), οι τιμές τύπου
strπαραμένουν αμετάβλητες.
Τα globals και locals περνιούνται στην
eval()· δείτε την τεκμηρίωση τηςeval()για περισσότερες πληροφορίες. Αν το globals ή το locals είναιNone, αυτή η συνάρτηση μπορεί να αντικαταστήσει αυτή την τιμή με μια προεπιλογή ειδική για το πλαίσιο, εξαρτώμενη από τοtype(obj):Αν το obj είναι module, η προεπιλογή για το globals είναι
obj.__dict__.Αν το obj είναι κλάση, η προεπιλογή για το globals είναι
sys.modules[obj.__module__].__dict__και η προεπιλογή για το locals είναι ο χώρος ονομάτων κλάσης του obj.Αν το obj είναι καλέσιμο, η προεπιλογή για το globals είναι
obj.__globals__, παρόλο που αν το obj είναι μια περιτυλιγμένη συνάρτηση (χρησιμοποιώνταςfunctools.update_wrapper()) ή ένα αντικείμενο τηςfunctools.partial, ξετυλίγεται μέχρι να βρεθεί μια μη περιτυλιγμένη συνάρτηση.
Η κλήση της
get_annotations()είναι η βέλτιστη πρακτική για την πρόσβαση στο λεξικό των επισημάνσεων (annotations) οποιουδήποτε αντικειμένου. Δείτε το Annotations Best Practices για περισσότερες πληροφορίες σχετικά με τις βέλτιστες πρακτικές των επισημάνσεων (annotations).>>> def f(a: int, b: str) -> float: ... pass >>> get_annotations(f) {'a': <class 'int'>, 'b': <class 'str'>, 'return': <class 'float'>}
Added in version 3.14.
- annotationlib.type_repr(value)¶
Μετατρέπει μια αυθαίρετη τιμή Python σε μια μορφή κατάλληλη για χρήση από τη μορφή
STRING. Αυτή καλεί τηνrepr()για τα περισσότερα αντικείμενα, αλλά έχει ειδικό χειρισμό για ορισμένα αντικείμενα, όπως τα αντικείμενα τύπου.Αυτό προορίζεται ως βοηθητική λειτουργία για συναρτήσεις annotate παρεχόμενες από τον χρήστη που υποστηρίζουν τη μορφή
STRING, αλλά δεν έχουν πρόσβαση στον κώδικα που δημιουργεί τις επισημάνσεις (annotations). Μπορεί επίσης να χρησιμοποιηθεί για να παρέχει μια φιλική προς τον χρήστη αναπαράσταση συμβολοσειράς για άλλα αντικείμενα που περιέχουν τιμές που συναντώνται συνήθως σε επισημάνσεις (annotations).Added in version 3.14.
Συνταγές¶
Χρήση των επισημάνσεων (annotations) σε μετακλάση¶
Μια μετακλάση μπορεί να θέλει να επιθεωρήσει ή ακόμη και να τροποποιήσει τις επισημάνσεις (annotations) στο σώμα μιας κλάσης κατά τη δημιουργία της κλάσης. Αυτό απαιτεί την ανάκτηση των επισημάνσεων (annotations) από το λεξικό χώρου ονομάτων της κλάσης. Για κλάσεις που δημιουργούνται με το from __future__ import annotations, οι επισημάνσεις (annotations) θα βρίσκονται στο κλειδί __annotations__ του λεξικού. Για άλλες κλάσεις με επισημάνσεις (annotations), η get_annotate_from_class_namespace() μπορεί να χρησιμοποιηθεί για να πάρει τη συνάρτηση annotate, και η call_annotate_function() μπορεί να χρησιμοποιηθεί για να την καλέσει και να ανακτήσει τις επισημάνσεις (annotations). Η χρήση της μορφής FORWARDREF συνήθως θα είναι η καλύτερη, επειδή επιτρέπει στις επισημάνσεις (annotations) να αναφέρονται σε ονόματα που δεν μπορούν ακόμη να επιλυθούν όταν δημιουργείται η κλάση.
Για να τροποποιήσετε τις επισημάνσεις (annotations), είναι καλύτερο να δημιουργήσετε μια περιτυλιγμένη συνάρτηση annotate που καλεί την αρχική συνάρτηση annotate, κάνει τις απαραίτητες προσαρμογές και επιστρέφει το αποτέλεσμα.
Παρακάτω είναι ένα παράδειγμα μιας μετακλάσης, που φιλτράρει όλες τις επισημάνσεις (annotations) typing.ClassVar από την κλάση και τα τοποθετεί σε ένα ξεχωριστό χαρακτηριστικό:
import annotationlib
import typing
class ClassVarSeparator(type):
def __new__(mcls, name, bases, ns):
if "__annotations__" in ns: # from __future__ import annotations
annotations = ns["__annotations__"]
classvar_keys = {
key for key, value in annotations.items()
# Use string comparison for simplicity; a more robust solution
# could use annotationlib.ForwardRef.evaluate
if value.startswith("ClassVar")
}
classvars = {key: annotations[key] for key in classvar_keys}
ns["__annotations__"] = {
key: value for key, value in annotations.items()
if key not in classvar_keys
}
wrapped_annotate = None
elif annotate := annotationlib.get_annotate_from_class_namespace(ns):
annotations = annotationlib.call_annotate_function(
annotate, format=annotationlib.Format.FORWARDREF
)
classvar_keys = {
key for key, value in annotations.items()
if typing.get_origin(value) is typing.ClassVar
}
classvars = {key: annotations[key] for key in classvar_keys}
def wrapped_annotate(format):
annos = annotationlib.call_annotate_function(annotate, format, owner=typ)
return {key: value for key, value in annos.items() if key not in classvar_keys}
else: # no annotations
classvars = {}
wrapped_annotate = None
typ = super().__new__(mcls, name, bases, ns)
if wrapped_annotate is not None:
# Wrap the original __annotate__ with a wrapper that removes ClassVars
typ.__annotate__ = wrapped_annotate
typ.classvars = classvars # Store the ClassVars in a separate attribute
return typ
Creating a custom callable annotate function¶
Custom annotate functions may be literal functions like those automatically generated for functions, classes, and modules. Or, they may wish to utilise the encapsulation provided by classes, in which case any callable can be used as an annotate function.
To provide the VALUE, STRING, or
FORWARDREF formats directly, an annotate function must provide
the following attribute:
A callable
__call__with signature__call__(format, /) -> dict, that does not raise aNotImplementedErrorwhen called with a supported format.
To provide the VALUE_WITH_FAKE_GLOBALS format, which is used to
automatically generate STRING or FORWARDREF if they are
not supported directly, annotate functions must provide the
following attributes:
A callable
__call__with signature__call__(format, /) -> dict, that does not raise aNotImplementedErrorwhen called withVALUE_WITH_FAKE_GLOBALS.A code object
__code__containing the compiled code for the annotate function.Optional: A tuple of the function’s positional defaults
__kwdefaults__, if the function represented by__code__uses any positional defaults.Optional: A dict of the function’s keyword defaults
__defaults__, if the function represented by__code__uses any keyword defaults.Optional: All other function attributes.
class Annotate:
called_formats = []
def __call__(self, format=None, /, *, _self=None):
# When called with fake globals, `_self` will be the
# actual self value, and `self` will be the format.
if _self is not None:
self, format = _self, self
self.called_formats.append(format)
if format <= 2: # VALUE or VALUE_WITH_FAKE_GLOBALS
return {"x": MyType}
raise NotImplementedError
__code__ = __call__.__code__
__defaults__ = (None,)
__kwdefaults__ = property(lambda self: dict(_self=self))
__globals__ = {}
__builtins__ = {}
__closure__ = None
This can then be called with:
>>> from annotationlib import call_annotate_function, Format
>>> call_annotate_function(Annotate(), format=Format.STRING)
{'x': 'MyType'}
Or used as the annotate function for an object:
>>> from annotationlib import get_annotations, Format
>>> class C:
... pass
>>> C.__annotate__ = Annotate()
>>> get_annotations(Annotate(), format=Format.STRING)
{'x': 'MyType'}
Περιορισμοί της μορφής STRING¶
Η μορφή STRING προορίζεται για να προσεγγίζει τον πηγαίο κώδικα του σχολιασμού, αλλά η στρατηγική υλοποίησης που χρησιμοποιείται σημαίνει ότι δεν είναι πάντα δυνατή η ανάκτηση του ακριβούς πηγαίου κώδικα.
Πρώτον, ο μετατροπέας συμβολοσειράς φυσικά δεν μπορεί να ανακτήσει πληροφορίες που δεν υπάρχουν στον μεταγλωττισμένο κώδικα, συμπεριλαμβανομένων των σχολίων, των λευκών χαρακτήρων, των παρενθέσεων και των πράξεων που απλοποιούνται από τον μεταγλωττιστή.
Δεύτερον, ο μετατροπέας συμβολοσειράς μπορεί να παρεμβάλει σχεδόν όλες τις πράξεις που περιλαμβάνουν ονόματα που αναζητούνται σε κάποια εμβέλεια, αλλά δεν μπορεί να παρεμβάλει πράξεις που λειτουργούν πλήρως πάνω σε σταθερές. Ως επακόλουθο, αυτό σημαίνει επίσης ότι δεν είναι ασφαλές να ζητείται η μορφή STRING σε μη αξιόπιστο κώδικα: η Python είναι αρκετά ισχυρή ώστε να μπορεί να επιτευχθεί αυθαίρετη εκτέλεση κώδικα ακόμη και χωρίς πρόσβαση σε global ή builtins. Για παράδειγμα:
>>> def f(x: (1).__class__.__base__.__subclasses__()[-1].__init__.__builtins__["print"]("Hello world")): pass
...
>>> annotationlib.get_annotations(f, format=annotationlib.Format.STRING)
Hello world
{'x': 'None'}
Σημείωση
Αυτό το συγκεκριμένο παράδειγμα λειτουργεί κατά τη στιγμή της συγγραφής, αλλά βασίζεται σε λεπτομέρειες υλοποίησης και δεν είναι εγγυημένο ότι θα λειτουργεί στο μέλλον.
Μεταξύ των διαφορετικών ειδών εκφράσεων που υπάρχουν στην Python, όπως αναπαρίστανται από το module ast, ορισμένες εκφράσεις υποστηρίζονται, που σημαίνει ότι η μορφή STRING μπορεί γενικά να ανακτήσει τον αρχικό πηγαίο κώδικα· άλλες δεν υποστηρίζονται, που σημαίνει ότι μπορεί να οδηγήσουν σε λανθασμένη έξοδο ή σφάλμα.
Τα ακόλουθα υποστηρίζονται (μερικές φορές με επιφυλάξεις):
-
Οι
ast.Invert(~),ast.UAdd(+) καιast.USub(-) υποστηρίζονταιΗ
ast.Not(not) δεν υποστηρίζεται
ast.Dict(εκτός αν χρησιμοποιείται αποσυσκευασία**)ast.Call(εκτός αν χρησιμοποιείται αποσυσκευασία**)ast.Constant(αν και όχι η ακριβής αναπαράσταση της σταθεράς· για παράδειγμα, οι ακολουθίες διαφυγής σε συμβολοσειρές χάνονται· οι δεκαεξαδικοί αριθμοί μετατρέπονται σε δεκαδικούς)ast.Attribute(υποθέτοντας ότι η τιμή δεν είναι σταθερά)ast.Subscript(υποθέτοντας ότι η τιμή δεν είναι σταθερά)ast.Starred(αποσυσκευασία*)
Τα ακόλουθα δεν υποστηρίζονται, αλλά εγείρει ένα ενημερωτικό σφάλμα όταν συναντώνται από τον μετατροπέα συμβολοσειράς:
ast.FormattedValue(f-strings· το σφάλμα δεν ανιχνεύεται αν χρησιμοποιούνται προδιαγραφές μετατροπής όπως!r)ast.JoinedStr(f-strings)
Τα ακόλουθα δεν υποστηρίζονται και οδηγούν σε λανθασμένη έξοδο:
Τα ακόλουθα δεν επιτρέπονται σε εμβέλειες των επισημάνσεων (annotations) και επομένως δεν είναι σχετικά:
Περιορισμοί της μορφής FORWARDREF¶
Η μορφή FORWARDREF στοχεύει στην παραγωγή πραγματικών τιμών όσο το δυνατόν περισσότερο, αντικαθιστώντας οτιδήποτε δεν μπορεί να επιλυθεί με αντικείμενα της ForwardRef. Επηρεάζεται γενικά από τους ίδιους περιορισμούς με τη μορφή STRING: οι επισημάνσεις (annotations) που εκτελούν πράξεις σε λεκτικές σταθερές ή που χρησιμοποιούν μη υποστηριζόμενους τύπους εκφράσεων μπορεί να κάνουν raise εξαιρέσεις όταν αξιολογούνται χρησιμοποιώντας τη μορφή FORWARDREF.
Παρακάτω είναι μερικά παραδείγματα της συμπεριφοράς με μη υποστηριζόμενες εκφράσεις:
>>> from annotationlib import get_annotations, Format
>>> def zerodiv(x: 1 / 0): ...
>>> get_annotations(zerodiv, format=Format.STRING)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
>>> get_annotations(zerodiv, format=Format.FORWARDREF)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
>>> def ifexp(x: 1 if y else 0): ...
>>> get_annotations(ifexp, format=Format.STRING)
{'x': '1'}
Επιπτώσεις ασφαλείας από την ενδοσκόπηση των επισημάνσεων (annotations)¶
Η περισσότερη λειτουργικότητα σε αυτό το module περιλαμβάνει την εκτέλεση κώδικα που σχετίζεται με επισημάνσεις (annotations), ο οποίος μπορεί στη συνέχεια να κάνει αυθαίρετα πράγματα. Για παράδειγμα, η get_annotations() μπορεί να καλέσει μια αυθαίρετη συνάρτηση annotate, και η ForwardRef.evaluate() μπορεί να καλέσει την eval() σε μια αυθαίρετη συμβολοσειρά. Ο κώδικας που περιέχεται σε μια επισήμανση (annotation) μπορεί να κάνει αυθαίρετες κλήσεις συστήματος, να μπει σε ατέρμονο βρόχο ή να εκτελέσει οποιαδήποτε άλλη λειτουργία. Αυτό ισχύει επίσης για κάθε πρόσβαση στο χαρακτηριστικό __annotations__, και για διάφορες συναρτήσεις στο module typing που εργάζονται με επισημάνσεις (annotations), όπως η typing.get_type_hints().
Κάθε ζήτημα ασφαλείας, που προκύπτει από αυτό, ισχύει επίσης αμέσως μετά την εισαγωγή κώδικα που μπορεί να περιέχει μη αξιόπιστες επισημάνσεις (annotations): η εισαγωγή κώδικα μπορεί πάντα να προκαλέσει την εκτέλεση αυθαίρετων λειτουργιών. Ωστόσο, είναι μη ασφαλές να γίνονται αποδεκτές συμβολοσειρές ή άλλη είσοδος από μη αξιόπιστη πηγή και να περνιούνται σε οποιοδήποτε από τα API για ενδοσκόπηση των επισημάνσεων (annotations), για παράδειγμα με την επεξεργασία ενός λεξικού __annotations__ ή την άμεση δημιουργία ενός αντικειμένου της ForwardRef.