8. Σύνθετες εντολές¶
Οι σύνθετες εντολές περιέχουν (ομάδες από) άλλες εντολές· επηρεάζουν ή ελέγχουν την εκτέλεση αυτών των άλλων εντολών με κάποιο τρόπο. Γενικά, οι σύνθετες εντολές εκτείνονται σε πολλαπλές γραμμές, αν και σε απλές μορφές μια ολόκληρη σύνθετη δήλωση μπορεί να περιέχεται σε μία γραμμή.
Οι εντολές if, while και for υλοποιούν παραδοσιακές δομές ροής ελέγχου. Η try καθορίζει χειριστές εξαιρέσεων ή/και κώδικα εκκαθάρισης για μια ομάδα εντολών, ενώ η δήλωση with επιτρέπει την εκτέλεση κώδικα αρχικοποίησης και οριστικοποίησης γύρω από ένα μπλοκ κώδικα. Οι ορισμοί συναρτήσεων και κλάσεων είναι επίσης συντακτικά σύνθετες εντολές.
Μια σύνθετη δήλωση αποτελείται από μία ή περισσότερες ‘ρήτρες’. Μία ρήτρα αποτελείται από μια κεφαλίδα και μία ‘σουίτα’. Οι κεφαλίδες των ρητρών μιας συγκεκριμένης σύνθετης εντολής βρίσκονται όλες στο ίδιο επίπεδο εσοχής. Κάθε κεφαλίδα της ρήτρας ξεκινά με μια μοναδική λέξη-κλειδί αναγνώρισης και καταλήγει σε άνω και κάτω τελεία. Μία σουίτα είναι μια ομάδα εντολών που ελέγχονται από μία ρήτρα. Μία σουίτα μπορεί να είναι μία ή περισσότερες απλές εντολές χωρισμένες με ερωτηματικό στην ίδια γραμμή με την κεφαλίδα, ακολουθώντας την άνω και κάτω τελεία της κεφαλίδας, ή μπορεί να είναι μία ή περισσότερες εντολές με εσοχή σε επόμενες γραμμές. Μόνο η δεύτερη μορφή μιας σουίτας μπορεί να περιέχει εμφωλευμένες σύνθετες εντολές· το ακόλουθο δεν είναι έγκυρο, κυρίως επειδή δεν θα ήταν σαφές σε ποια ρήτρα if θα ανήκε μία ακόλουθη ρήτρα else:
if test1: if test2: print(x)
Σημειώστε επίσης ότι το ερωτηματικό έχει ισχυρότερη προτεραιότητα από την άνω και κάτω τελεία σε αυτό το πλαίσιο, οπότε στο ακόλουθο παράδειγμα, εκτελούνται είτε όλες είτε καμία από τις κλήσεις της print():
if x < y < z: print(x); print(y); print(z)
Συνοψίζοντας:
compound_stmt:if_stmt|while_stmt|for_stmt|try_stmt|with_stmt|match_stmt|funcdef|classdef|async_with_stmt|async_for_stmt|async_funcdefsuite:stmt_listNEWLINE | NEWLINE INDENTstatement+ DEDENT statement:stmt_listNEWLINE |compound_stmtstmt_list:simple_stmt(";"simple_stmt)* [";"]
Σημειώστε ότι οι εντολές τελειώνουν πάντα με ένα NEWLINE πιθανώς ακολουθούμενο από ένα DEDENT. Σημειώστε επίσης ότι οι προαιρετικές ρήτρες συνέχειας ξεκινούν πάντα με μια λέξη-κλειδί που δεν μπορεί να ξεκινήσει μια δήλωση, επομένως δεν υπάρχουν ασάφειες (το πρόβλημα του “εκκρεμούς else” λύνεται στην Python απαιτώντας οι εμφωλευμένες εντολές if να έχουν εσοχή).
Η μορφοποίηση των γραμματικών κανόνων στις ακόλουθες ενότητες τοποθετεί κάθε ρήτρα σε ξεχωριστή γραμμή για λόγους σαφήνειας.
8.1. Η δήλωση if¶
Η δήλωση if χρησιμοποιείται για εκτέλεση υπό συνθήκη:
if_stmt: "if"assignment_expression":"suite("elif"assignment_expression":"suite)* ["else" ":"suite]
Επιλέγει ακριβώς μία από τις σουίτες αξιολογώντας τις εκφράσεις μία προς μία μέχρι να βρεθεί μία που να είναι αληθής (δείτε την ενότητα Λογικές λειτουργίες για τον ορισμό του αληθούς και του ψευδούς)· στη συνέχεια εκτελείται αυτή η σουίτα (και κανένα άλλο μέρος της εντολής if δεν εκτελείται ή αξιολογείται). Εάν όλες οι εκφράσεις είναι ψευδείς, εκτελείται η σουίτα της ρήτρας else, εάν υπάρχει.
8.2. Η δήλωση while¶
Η δήλωση while χρησιμοποιείται για επαναλαμβανόμενη εκτέλεση εφόσον μια έκφραση είναι αληθής:
while_stmt: "while"assignment_expression":"suite["else" ":"suite]
Ελέγχει επανειλημμένα την έκφραση και, εάν είναι αληθής, εκτελεί την πρώτη σουίτα· εάν η έκφραση είναι ψευδής (το οποίο μπορεί να συμβεί την πρώτη φορά που ελέγχεται) εκτελείται η σουίτα της ρήτρας else, εάν υπάρχει, και ο βρόχος τερματίζεται.
Μια δήλωση break που εκτελείται στην πρώτη σουίτα τερματίζει τον βρόχο χωρίς να εκτελέσει τη σουίτα της ρήτρας else. Μια δήλωση continue που εκτελείται στην πρώτη σουίτα παραλείπει το υπόλοιπο της σουίτας και επιστρέφει στον έλεγχο της έκφρασης.
8.3. Η δήλωση for¶
Η δήλωση for χρησιμοποιείται για την επανάληψη στα στοιχεία μιας ακολουθίας (όπως ένα αλφαριθμητικό, μία πλειάδα ή μία λίστα) ή άλλου iterable αντικειμένου:
for_stmt: "for"target_list"in"starred_expression_list":"suite["else" ":"suite]
Η έκφραση starred_expression_list αξιολογείται μία φορά· θα πρέπει να αποφέρει ένα αντικείμενο iterable. Ένας iterator δημιουργείται για αυτό το iterable. Το πρώτο στοιχείο που παρέχεται από τον iterator ανατίθεται στη συνέχεια στη λίστα-στόχο χρησιμοποιώντας τους τυπικούς κανόνες για αναθέσεις (δείτε το Εντολές ανάθεσης), και η σουίτα εκτελείται. Αυτό επαναλαμβάνεται για κάθε στοιχείο που παρέχεται από τον iterator. Όταν ο iterator εξαντληθεί, εκτελείται η σουίτα στη ρήτρα else, εάν υπάρχει, και ο βρόχος τερματίζεται.
Μια δήλωση break που εκτελείται στην πρώτη σουίτα τερματίζει τον βρόχο χωρίς να εκτελέσει τη σουίτα της ρήτρας else. Μια δήλωση continue που εκτελείται στην πρώτη σουίτα παραλείπει το υπόλοιπο της σουίτας και συνεχίζει με το επόμενο στοιχείο, ή με τη ρήτρα else εάν δεν υπάρχει επόμενο στοιχείο.
Ο βρόχος for κάνει αναθέσεις στις μεταβλητές της λίστας στόχου. Αυτό αντικαθιστά όλες τις προηγούμενες αναθέσεις σε αυτές τις μεταβλητές συμπεριλαμβανομένων αυτών που έγιναν στη σουίτα του βρόχου for:
for i in range(10):
print(i)
i = 5 # this will not affect the for-loop
# because i will be overwritten with the next
# index in the range
Τα ονόματα στη λίστα-στόχο δεν διαγράφονται όταν τελειώσει ο βρόχος, αλλά εάν η ακολουθία είναι κενή, δεν θα τους έχει γίνει καμία ανάθεση από τον βρόχο. Υπόδειξη: ο ενσωματωμένος τύπος range() αναπαριστά αμετάβλητες αριθμητικές ακολουθίες ακεραίων. Για παράδειγμα, η επανάληψη του range(3) διαδοχικά αποφέρει 0, 1, και έπειτα 2.
Άλλαξε στην έκδοση 3.11: Τα στοιχεία με αστερίσκο επιτρέπονται πλέον στη λίστα εκφράσεων.
8.4. Η δήλωση try¶
Η δήλωση try καθορίζει χειριστές εξαιρέσεων ή/και κώδικα εκκαθάρισης για μια ομάδα εντολών:
try_stmt:try1_stmt|try2_stmt|try3_stmttry1_stmt: "try" ":"suite("except" [expression["as"identifier]] ":"suite)+ ["else" ":"suite] ["finally" ":"suite] try2_stmt: "try" ":"suite("except" "*"expression["as"identifier] ":"suite)+ ["else" ":"suite] ["finally" ":"suite] try3_stmt: "try" ":"suite"finally" ":"suite
Πρόσθετες πληροφορίες σχετικά με τις εξαιρέσεις μπορούν να βρεθούν στην ενότητα Εξαιρέσεις, και πληροφορίες σχετικά με τη χρήση της εντολής raise για τη δημιουργία εξαιρέσεων μπορούν να βρεθούν στην ενότητα Η εντολή raise.
Άλλαξε στην έκδοση 3.14: Υποστήριξη για προαιρετική αφαίρεση των παρενθέσεων ομαδοποίησης όταν χρησιμοποιούνται πολλαπλοί τύποι εξαιρέσεων. Δείτε το PEP 758.
8.4.1. ρήτρα except¶
Οι ρήτρες except καθορίζουν έναν ή περισσότερους χειριστές εξαιρέσεων. Όταν δεν προκύπτει καμία εξαίρεση στη ρήτρα try, κανένας χειριστής εξαίρεσης δεν εκτελείται. Όταν προκύψει μια εξαίρεση στη σουίτα του try, ξεκινά μια αναζήτηση για χειριστή εξαίρεσης. Αυτή η αναζήτηση εξετάζει τις ρήτρες except με τη σειρά μέχρι να βρεθεί μία που ταιριάζει με την εξαίρεση. Μία ρήτρα except χωρίς έκφραση, εάν υπάρχει, πρέπει να είναι τελευταία· ταιριάζει με οποιαδήποτε εξαίρεση.
Για μία ρήτρα except με μια έκφραση, η έκφραση πρέπει να αξιολογείται σε έναν τύπο εξαίρεσης ή σε μία πλειάδα τύπων εξαιρέσεων. Οι παρενθέσεις μπορούν να παραλειφθούν εάν παρέχονται πολλαπλοί τύποι εξαιρέσεων και η ρήτρα as δεν χρησιμοποιείται. Η εξαίρεση που έγινε raise ταιριάζει με μία ρήτρα except του οποίου η έκφραση αξιολογείται στην κλάση ή σε μια μη εικονική βασική κλάση του αντικειμένου εξαίρεσης, ή σε μία πλειάδα που περιέχει μια τέτοια κλάση.
Εάν καμία ρήτρα except δεν ταιριάζει με την εξαίρεση, η αναζήτηση για έναν χειριστή εξαίρεσης συνεχίζεται στον περιβάλλοντα κώδικα και στη στοίβα κλήσεων. [1]
Εάν η αξιολόγηση μιας έκφρασης στην κεφαλίδα μιας ρήτρας except κάνει raise μια εξαίρεση, η αρχική αναζήτηση για χειριστή ακυρώνεται και μια αναζήτηση ξεκινά για τη νέα εξαίρεση στον περιβάλλοντα κώδικα και στη στοίβα κλήσεων (αντιμετωπίζεται σαν να έκανε raise την εξαίρεση ολόκληρη η δήλωση try).
Όταν βρεθεί μία ρήτρα except που να ταιριάζει, η εξαίρεση ανατίθεται στον στόχο που καθορίζεται μετά τη λέξη-κλειδί as σε αυτή τη ρήτρα except, εάν υπάρχει, και η σουίτα της ρήτρας except εκτελείται. Όλες οι ρήτρες except πρέπει να έχουν ένα εκτελέσιμο μπλοκ. Όταν επιτευχθεί το τέλος αυτού του μπλοκ, η εκτέλεση συνεχίζεται κανονικά μετά από ολόκληρη την δήλωση try. (Αυτό σημαίνει ότι εάν υπάρχουν δύο εμφωλευμένοι χειριστές για την ίδια εξαίρεση, και η εξαίρεση συμβεί στη ρήτρα try του εσωτερικού χειριστή, ο εξωτερικός χειριστής δεν θα χειριστεί την εξαίρεση.)
Όταν μια εξαίρεση έχει ανατεθεί χρησιμοποιώντας το as target, καθαρίζεται στο τέλος της ρήτρας except. Αυτό είναι σαν το
except E as N:
foo
να είχε μεταφραστεί σε
except E as N:
try:
foo
finally:
del N
Αυτό σημαίνει ότι η εξαίρεση πρέπει να ανατεθεί σε διαφορετικό όνομα για να μπορείτε να αναφερθείτε σε αυτήν μετά τη ρήτρα except. Οι εξαιρέσεις καθαρίζονται επειδή με την ανίχνευση επισυναπτόμενη σε αυτές, σχηματίζουν έναν κύκλο αναφοράς με το πλαίσιο στοίβας, διατηρώντας όλες τις τοπικές μεταβλητές σε αυτό το πλαίσιο ζωντανές μέχρι να συμβεί η επόμενη συλλογή απορριμμάτων.
Πριν εκτελεστεί η σουίτα μιας ρήτρας except, η εξαίρεση αποθηκεύεται στο module sys, όπου είναι προσβάσιμη μέσα από το σώμα της ρήτρας except καλώντας την sys.exception(). Όταν κλείνει ένας χειριστής εξαίρεσης, η εξαίρεση που αποθηκεύτηκε στο module sys επαναφέρεται στην προηγούμενη τιμή της:
>>> print(sys.exception())
None
>>> try:
... raise TypeError
... except:
... print(repr(sys.exception()))
... try:
... raise ValueError
... except:
... print(repr(sys.exception()))
... print(repr(sys.exception()))
...
TypeError()
ValueError()
TypeError()
>>> print(sys.exception())
None
8.4.2. ρήτρα except*¶
Οι ρήτρες except* καθορίζουν έναν ή περισσότερους χειριστές για ομάδες εξαιρέσεων (στιγμιότυπα της BaseExceptionGroup). Μια δήλωση try μπορεί να έχει είτε ρήτρες except είτε ρήτρες except*, αλλά όχι και τα δύο. Ο τύπος εξαίρεσης για το ταίριασμα είναι υποχρεωτικός στην περίπτωση του except*, επομένως το except*: είναι συντακτικό σφάλμα. Ο τύπος ερμηνεύεται όπως και στην περίπτωση του except, αλλά το ταίριασμα εκτελείται στις εξαιρέσεις που περιέχονται στην ομάδα που τυγχάνει χειρισμού. Μια TypeError γίνεται raise εάν ένας τύπος που ταιριάζει είναι υποκλάση του BaseExceptionGroup, επειδή αυτό θα είχε διφορούμενη σημασιολογία.
Όταν μια ομάδα εξαιρέσεων γίνει raise στο μπλοκ try, κάθε ρήτρα except* τη διαχωρίζει (δείτε τη split()) στις υπο-ομάδες ταιριαστών και μη ταιριαστών εξαιρέσεων. Εάν η ταιριαστή υπο-ομάδα δεν είναι κενή, γίνεται η εξαίρεση υπό χειρισμό (η τιμή που επιστρέφεται από την sys.exception()) και ανατίθεται στον στόχο της ρήτρας except* (εάν υπάρχει). Έπειτα, εκτελείται το σώμα της ρήτρας except*. Εάν η μη ταιριαστή υπο-ομάδα δεν είναι κενή, επεξεργάζεται από το επόμενο except* με τον ίδιο τρόπο. Αυτό συνεχίζεται μέχρι να ταιριάξουν όλες οι εξαιρέσεις στην ομάδα, ή να εκτελεστεί η τελευταία ρήτρα except*.
Αφού εκτελεστούν όλες οι ρήτρες except*, η ομάδα των μη χειριζόμενων εξαιρέσεων συγχωνεύεται με τυχόν εξαιρέσεις που έγιναν raise ή re-raise μέσα από ρήτρες except*. Αυτή η συγχωνευμένη ομάδα εξαιρέσεων συνεχίζει να διαδίδεται.:
>>> try:
... raise ExceptionGroup("eg",
... [ValueError(1), TypeError(2), OSError(3), OSError(4)])
... except* TypeError as e:
... print(f'caught {type(e)} with nested {e.exceptions}')
... except* OSError as e:
... print(f'caught {type(e)} with nested {e.exceptions}')
...
caught <class 'ExceptionGroup'> with nested (TypeError(2),)
caught <class 'ExceptionGroup'> with nested (OSError(3), OSError(4))
+ Exception Group Traceback (most recent call last):
| File "<doctest default[0]>", line 2, in <module>
| raise ExceptionGroup("eg",
| [ValueError(1), TypeError(2), OSError(3), OSError(4)])
| ExceptionGroup: eg (1 sub-exception)
+-+---------------- 1 ----------------
| ValueError: 1
+------------------------------------
Εάν η εξαίρεση που έγινε raise από το μπλοκ try δεν είναι ομάδα εξαιρέσεων και ο τύπος της ταιριάζει με μία από τις ρήτρες except*, τότε πιάνεται και περιτυλίγεται από μια ομάδα εξαιρέσεων με μία κενή συμβολοσειρά μηνύματος. Αυτό διασφαλίζει ότι ο τύπος του στόχου e είναι συνεπώς BaseExceptionGroup:
>>> try:
... raise BlockingIOError
... except* BlockingIOError as e:
... print(repr(e))
...
ExceptionGroup('', (BlockingIOError(),))
Τα break, continue και return δεν μπορούν να εμφανιστούν σε μία ρήτρα except*.
8.4.3. ρήτρα else¶
Η προαιρετική ρήτρα else εκτελείται εάν η ροή ελέγχου αφήσει τη σουίτα try, δεν έγινε raise καμία εξαίρεση, και δεν εκτελέστηκε καμία δήλωση return, continue, ή break. Οι εξαιρέσεις στη ρήτρα else δεν χειρίζονται από τις προηγούμενες ρήτρες except.
8.4.4. ρήτρα finally¶
Εάν το finally υπάρχει, καθορίζει έναν χειριστή “εκκαθάρισης”. Η ρήτρα try εκτελείται, συμπεριλαμβανομένων τυχόν ρητρών except και else. Εάν προκύψει εξαίρεση σε κάποια από τις ρήτρες και δεν υπάρξει χειρισμός, η εξαίρεση αποθηκεύεται προσωρινά. Η ρήτρα finally εκτελείται. Εάν υπάρχει μια αποθηκευμένη εξαίρεση, γίνεται re-raise στο τέλος της ρήτρας finally. Εάν η ρήτρα finally κάνει raise μια άλλη εξαίρεση, η αποθηκευμένη εξαίρεση ορίζεται ως το πλαίσιο της νέας εξαίρεσης. Εάν η ρήτρα finally εκτελεί μια δήλωση return, break ή continue, η αποθηκευμένη εξαίρεση απορρίπτεται. Για παράδειγμα, αυτή η συνάρτηση επιστρέφει 42.
def f():
try:
1/0
finally:
return 42
Οι πληροφορίες εξαίρεσης δεν είναι διαθέσιμες στο πρόγραμμα κατά την εκτέλεση της ρήτρας finally.
Όταν μια δήλωση return, break ή continue εκτελείται στη σουίτα try μιας εντολής try…finally, η ρήτρα finally εκτελείται επίσης “κατά την έξοδο”.
Η τιμή επιστροφής μιας συνάρτησης καθορίζεται από την τελευταία δήλωση return που εκτελέστηκε. Εφόσον η ρήτρα finally εκτελείται πάντα, μια δήλωση return που εκτελείται στη ρήτρα finally θα είναι πάντα η τελευταία που θα εκτελεστεί. Η ακόλουθη συνάρτηση επιστρέφει “finally”.
def foo():
try:
return 'try'
finally:
return 'finally'
Άλλαξε στην έκδοση 3.8: Πριν από την Python 3.8, μια δήλωση continue ήταν μη έγκυρη στη ρήτρα finally λόγω ενός προβλήματος με την υλοποίηση.
Άλλαξε στην έκδοση 3.14: Ο compiler εκπέμπει ένα SyntaxWarning όταν εμφανίζεται ένα return, break ή continue σε ένα μπλοκ finally (δείτε PEP 765).
8.5. Η δήλωση with¶
Η δήλωση with χρησιμοποιείται για να περιτυλίξει την εκτέλεση ενός μπλοκ με μεθόδους ορισμένες από έναν διαχειριστή πλαισίου (δείτε την ενότητα Με τους Διαχειριστές Περιβάλλοντος Δήλωσης). Αυτό επιτρέπει την ενθυλάκωση κοινών μοτίβων χρήσης try…except…finally για βολική επαναχρησιμοποίηση.
with_stmt: "with" ( "("with_stmt_contents","? ")" |with_stmt_contents) ":"suitewith_stmt_contents:with_item(","with_item)* with_item:expression["as"target]
Η εκτέλεση της εντολής with με ένα «αντικείμενο» προχωρά ως εξής:
Η έκφραση πλαισίου (η έκφραση που δίνεται στο
with_item) αξιολογείται για να ληφθεί ένας διαχειριστής πλαισίου.Φορτώνεται η
__enter__()του διαχειριστή πλαισίου για μελλοντική χρήση.Φορτώνεται η
__exit__()του διαχειριστή πλαισίου για μελλοντική χρήση.Καλείται η μέθοδος
__enter__()του διαχειριστή πλαισίου.Εάν συμπεριλήφθηκε ένας στόχος στην δήλωση
with, η τιμή επιστροφής της__enter__()του ανατίθεται.Σημείωση
Η δήλωση
withεγγυάται ότι εάν η μέθοδος__enter__()επιστρέψει το αποτέλεσμα χωρίς σφάλμα, τότε η__exit__()θα κληθεί πάντα. Επομένως, εάν προκύψει σφάλμα κατά την ανάθεση στη λίστα-στόχο, θα αντιμετωπιστεί με τον ίδιο τρόπο όπως ένα σφάλμα που προκύπτει μέσα στη σουίτα. Δείτε το βήμα 7 παρακάτω.Η σουίτα εκτελείται.
Καλείται η μέθοδος
__exit__()του διαχειριστή πλαισίου. Εάν μια εξαίρεση προκάλεσε την έξοδο από τη σουίτα, ο τύπος, η τιμή και η ιχνηλάτησή της περνούν ως ορίσματα στην__exit__(). Διαφορετικά, παρέχονται τρία ορίσματαNone.Εάν υπήρξε έξοδος από τη σουίτα λόγω εξαίρεσης, και η τιμή επιστροφής από τη μέθοδο
__exit__()ήταν false, η εξαίρεση γίνεται re-raise. Εάν η τιμή επιστροφής ήταν true, η εξαίρεση καταστέλλεται, και η εκτέλεση συνεχίζεται με την δήλωση που ακολουθεί την δήλωσηwith.Εάν υπήρξε έξοδος από τη σουίτα για οποιονδήποτε λόγο εκτός από εξαίρεση, η τιμή επιστροφής από την
__exit__()αγνοείται, και η εκτέλεση προχωρά στην κανονική θέση για το είδος της εξόδου που πραγματοποιήθηκε.
Ο ακόλουθος κώδικας:
with EXPRESSION as TARGET:
SUITE
είναι σημασιολογικά ισοδύναμος με:
manager = (EXPRESSION)
enter = manager.__enter__
exit = manager.__exit__
value = enter()
hit_except = False
try:
TARGET = value
SUITE
except:
hit_except = True
if not exit(*sys.exc_info()):
raise
finally:
if not hit_except:
exit(None, None, None)
except that implicit special method lookup is used
for __enter__() and __exit__().
Με περισσότερα από ένα στοιχεία, οι διαχειριστές πλαισίου επεξεργάζονται σαν να είχαν εμφωλευτεί πολλαπλές εντολές with:
with A() as a, B() as b:
SUITE
είναι σημασιολογικά ισοδύναμος με:
with A() as a:
with B() as b:
SUITE
Μπορείτε επίσης να γράψετε διαχειριστές πλαισίου πολλαπλών στοιχείων σε πολλές γραμμές, αν τα στοιχεία περικλείονται σε παρενθέσεις. Για παράδειγμα:
with (
A() as a,
B() as b,
):
SUITE
Άλλαξε στην έκδοση 3.1: Υποστήριξη για πολλαπλές εκφράσεις πλαισίου.
Άλλαξε στην έκδοση 3.10: Υποστήριξη για χρήση παρενθέσεων ομαδοποίησης ώστε να διασπαστεί η δήλωση σε πολλές γραμμές.
8.6. Η δήλωση match¶
Added in version 3.10.
Η δήλωση match χρησιμοποιείται για ταίριασμα προτύπων. Σύνταξη:
match_stmt: 'match'subject_expr":" NEWLINE INDENTcase_block+ DEDENT subject_expr: `!star_named_expression` "," `!star_named_expressions`? | `!named_expression` case_block: 'case'patterns[guard] ":" `!block`
Σημείωση
Αυτή η ενότητα χρησιμοποιεί μονά εισαγωγικά για να υποδηλώσει τα soft keywords.
Το ταίριασμα προτύπων παίρνει ένα πρότυπο ως είσοδο (μετά το case) και μια τιμή θέματος (μετά το match). Το πρότυπο (το οποίο μπορεί να περιέχει υπο-πρότυπα) ταιριάζεται με την τιμή θέματος. Τα αποτελέσματα είναι:
Μια επιτυχία ή αποτυχία ταιριάσματος (επίσης ονομάζεται επιτυχία ή αποτυχία προτύπου).
Πιθανή δέσμευση ταιριασμένων τιμών σε ένα όνομα. Οι προϋποθέσεις για αυτό συζητούνται περαιτέρω παρακάτω.
Οι λέξεις-κλειδιά match και case είναι soft keywords.
Δείτε επίσης
8.6.1. Επισκόπηση¶
Ακολουθεί μια επισκόπηση της λογικής ροής μιας εντολής match:
Η έκφραση θέματος
subject_exprαξιολογείται και λαμβάνεται η προκύπτουσα τιμή θέματος. Εάν η έκφραση θέματος περιέχει κόμμα, κατασκευάζεται μία πλειάδα χρησιμοποιώντας τους τυπικούς κανόνες.Κάθε πρότυπο σε ένα
case_blockπροσπαθεί να ταιριάξει με την τιμή του θέματος. Οι συγκεκριμένοι κανόνες για επιτυχία ή αποτυχία περιγράφονται παρακάτω. Η προσπάθεια ταιριάσματος μπορεί επίσης να δεσμεύσει κάποια ή όλα τα αυτόνομα ονόματα εντός του προτύπου. Οι ακριβείς κανόνες δέσμευσης προτύπων διαφέρουν ανά τύπο προτύπου και καθορίζονται παρακάτω. Οι δεσμεύσεις ονομάτων, που γίνονται κατά τη διάρκεια ενός επιτυχημένου ταιριάσματος προτύπου, επιβιώνουν από το εκτελεσμένο μπλοκ και μπορούν να χρησιμοποιηθούν μετά την δήλωση match.Σημείωση
Κατά τη διάρκεια αποτυχημένων ταιριασμάτων προτύπων, ορισμένα υπο-πρότυπα μπορεί να πετύχουν. Μην βασίζεστε στο ότι θα πραγματοποιηθούν δεσμεύσεις για ένα αποτυχημένο ταίριασμα. Αντίστροφα, μην βασίζεστε στο ότι οι μεταβλητές θα παραμείνουν αμετάβλητες μετά από ένα αποτυχημένο ταίριασμα. Η ακριβής συμπεριφορά εξαρτάται από την υλοποίηση και μπορεί να διαφέρει. Πρόκειται για μια σκόπιμη απόφαση ώστε να επιτραπεί σε διαφορετικές υλοποιήσεις να προσθέσουν βελτιστοποιήσεις.
Εάν το πρότυπο επιτύχει, αξιολογείται ο αντίστοιχος guard (εάν υπάρχει). Σε αυτήν την περίπτωση είναι εγγυημένο ότι έχουν πραγματοποιηθεί όλες οι δεσμεύσεις ονομάτων.
Εάν ο guard αξιολογηθεί ως αληθής ή λείπει, εκτελείται το
blockμέσα στοcase_block.Διαφορετικά, δοκιμάζεται το επόμενο
case_blockόπως περιγράφεται παραπάνω.Εάν δεν υπάρχουν περαιτέρω case μπλοκ, η δήλωση match ολοκληρώνεται.
Σημείωση
Οι χρήστες γενικά δεν θα πρέπει ποτέ να βασίζονται στο ότι ένα πρότυπο αξιολογείται. Ανάλογα με την υλοποίηση, ο διερμηνέας μπορεί να αποθηκεύσει τιμές στην προσωρινή μνήμη ή να χρησιμοποιήσει άλλες βελτιστοποιήσεις που παραλείπουν επαναλαμβανόμενες αξιολογήσεις.
Ένα παράδειγμα εντολής match:
>>> flag = False
>>> match (100, 200):
... case (100, 300): # Mismatch: 200 != 300
... print('Case 1')
... case (100, 200) if flag: # Successful match, but guard fails
... print('Case 2')
... case (100, y): # Matches and binds y to 200
... print(f'Case 3, y: {y}')
... case _: # Pattern not attempted
... print('Case 4, I match anything!')
...
Case 3, y: 200
Σε αυτήν την περίπτωση, το if flag είναι ένας guard. Διαβάστε περισσότερα για αυτό στην επόμενη ενότητα.
8.6.2. Guards¶
guard: "if" `!named_expression`
Ένας guard (που αποτελεί μέρος του case) πρέπει να επιτύχει προκειμένου να εκτελεστεί ο κώδικας μέσα στο μπλοκ case. Παίρνει τη μορφή: if ακολουθούμενο από μια έκφραση.
Η λογική ροή ενός μπλοκ case με έναν guard έχει ως εξής:
Ελέγχει ότι το πρότυπο στο μπλοκ
caseεπέτυχε. Εάν το πρότυπο απέτυχε, οguardδεν αξιολογείται και ελέγχεται το επόμενο μπλοκcase.Εάν το πρότυπο επέτυχε, αξιολογείται ο
guard.Εάν η συνθήκη του
guardαξιολογηθεί ως αληθής, επιλέγεται το μπλοκ case.Εάν η συνθήκη του
guardαξιολογηθεί ως ψευδής, το μπλοκ case δεν επιλέγεται.Εάν ο
guardκάνει raise μια εξαίρεση κατά την αξιολόγηση, η εξαίρεση ανεβαίνει προς τα πάνω.
Οι guards επιτρέπεται να έχουν παρενέργειες καθώς είναι εκφράσεις. Η αξιολόγηση των guards πρέπει να προχωρά από το πρώτο έως το τελευταίο case μπλοκ, ένα προς ένα, παραλείποντας τα case μπλοκ των οποίων τα πρότυπα δεν επιτυγχάνουν όλα. (Δηλαδή, η αξιολόγηση των guards πρέπει να γίνει με τη σειρά.) Η αξιολόγηση των guards πρέπει να σταματήσει μόλις επιλεγεί ένα case μπλοκ.
8.6.3. Αδιάσειστα Case Blocks¶
Ένα αδιάσειστο case μπλοκ είναι ένα case μπλοκ που ταιριάζει με τα πάντα. Μια δήλωση match μπορεί να έχει το πολύ ένα αδιάσειστο case μπλοκ, και πρέπει να είναι το τελευταίο.
Ένα case μπλοκ θεωρείται αδιάσειστο εάν δεν έχει guard και το πρότυπό του είναι αδιάσειστο. Ένα πρότυπο θεωρείται αδιάσειστο εάν μπορούμε να αποδείξουμε μόνο από τη σύνταξή του ότι θα επιτυγχάνει πάντα. Μόνο τα ακόλουθα πρότυπα είναι αδιάσειστα:
AS Πρότυπα των οποίων η αριστερή πλευρά είναι αδιάσειστη
OR Πρότυπα που περιέχουν τουλάχιστον ένα αδιάσειστο πρότυπο
παρενθετικά αδιάσειστα πρότυπα
8.6.4. Πρότυπα¶
Σημείωση
Αυτή η ενότητα χρησιμοποιεί σημειογραφίες γραμματικής πέρα από το τυπικό EBNF:
η σημειογραφία
SEP.RULE+είναι συντομογραφία για τοRULE (SEP RULE)*η σημειογραφία
!RULEείναι συντομογραφία για έναν αρνητικό ισχυρισμό lookahead
Η σύνταξη ανωτέρου επιπέδου για τα patterns είναι:
patterns:open_sequence_pattern|patternpattern:as_pattern|or_patternclosed_pattern: |literal_pattern|capture_pattern|wildcard_pattern|value_pattern|group_pattern|sequence_pattern|mapping_pattern|class_pattern
Οι παρακάτω περιγραφές θα περιλαμβάνουν μια περιγραφή «με απλά λόγια» του τι κάνει ένα πρότυπο για σκοπούς επεξήγησης (ευχαριστίες στον Raymond Hettinger για ένα έγγραφο που ενέπνευσε τις περισσότερες περιγραφές). Σημειώστε ότι αυτές οι περιγραφές προορίζονται καθαρά για σκοπούς επεξήγησης και ενδέχεται να μην αντανακλούν την υποκείμενη υλοποίηση. Επιπλέον, δεν καλύπτουν όλες τις έγκυρες μορφές.
8.6.4.1. OR Πρότυπα¶
Ένα OR πρότυπο αποτελείται από δύο ή περισσότερα πρότυπα χωρισμένα με κάθετες γραμμές |. Σύνταξη:
or_pattern: "|".closed_pattern+
Μόνο το τελικό υπο-πρότυπο μπορεί να είναι αδιάσειστο, και κάθε υπο-πρότυπο πρέπει να δεσμεύει το ίδιο σύνολο ονομάτων για να αποφευχθεί η ασάφεια.
Ένα OR πρότυπο ταιριάζει με καθένα από τα υπο-πρότυπά του διαδοχικά με την τιμή του θέματος, μέχρι κάποιο να επιτύχει. Το OR πρότυπο θεωρείται τότε επιτυχημένο. Διαφορετικά, εάν κανένα από τα υπο-μοτίβα δεν επιτύχει, το OR πρότυπο αποτυγχάνει.
Με απλά λόγια, το P1 | P2 | ... θα προσπαθήσει να ταιριάξει το P1, εάν αποτύχει θα προσπαθήσει να ταιριάξει το P2, επιτυγχάνοντας αμέσως εάν οποιοδήποτε πετύχει, αποτυγχάνοντας διαφορετικά.
8.6.4.2. AS Πρότυπα¶
Ένα AS πρότυπο ταιριάζει με ένα OR πρότυπο στα αριστερά της λέξης-κλειδιού as έναντι ενός θέματος. Σύνταξη:
as_pattern:or_pattern"as"capture_pattern
Εάν το OR πρότυπο αποτύχει, το AS πρότυπο αποτυγχάνει. Διαφορετικά, το AS πρότυπο δεσμεύει το θέμα στο όνομα στα δεξιά της λέξης-κλειδιού as και επιτυγχάνει. Το capture_pattern δεν μπορεί να είναι _.
Με απλά λόγια, το P as NAME θα ταιριάξει με το P, και σε περίπτωση επιτυχίας θα ορίσει NAME = <subject>.
8.6.4.3. Πρότυπα Λεκτικών Σταθερών¶
Ένα πρότυπο λεκτικής σταθεράς αντιστοιχεί στα περισσότερα literals στην Python. Σύνταξη:
literal_pattern:signed_number|signed_number"+" NUMBER |signed_number"-" NUMBER |strings| "None" | "True" | "False" signed_number: ["+" | "-"] NUMBER
Ο κανόνας strings και το token NUMBER ορίζονται στο standard Python grammar. Υποστηρίζονται συμβολοσειρές με τριπλά εισαγωγικά. Υποστηρίζονται ακατέργαστες συμβολοσειρές και byte συμβολοσειρές. Τα f-strings και τα t-strings δεν υποστηρίζονται.
Οι μορφές signed_number '+' NUMBER και signed_number '-' NUMBER είναι για την έκφραση μιγαδικών αριθμών· απαιτούν έναν πραγματικό αριθμό αριστερά και έναν φανταστικό αριθμό δεξιά. Π.χ. 3 + 4j.
Με απλά λόγια, το LITERAL θα πετύχει μόνο εάν <subject> == LITERAL. Για τα singletons None, True και False, χρησιμοποιείται ο τελεστής is.
8.6.4.4. Μοτίβα Δέσμευσης¶
Ένα μοτίβο δέσμευσης δεσμεύει την τιμή του θέματος σε ένα όνομα. Σύνταξη:
capture_pattern: !'_' NAME
Μία μόνο κάτω παύλα _ δεν μοτίβο δέσμευσης (αυτό εκφράζει το !'_'). Αντιθέτως, αντιμετωπίζεται ως wildcard_pattern.
Σε ένα δεδομένο μοτίβο, ένα δεδομένο όνομα μπορεί να δεσμευτεί μόνο μία φορά. Π.χ. το case x, x: ... είναι μη έγκυρο, ενώ το case [x] | x: ... επιτρέπεται.
Τα μοτίβα δέσμευσης επιτυγχάνουν πάντα. Η δέσμευση ακολουθεί τους κανόνες εύρους που καθιερώθηκαν από τον τελεστή έκφρασης ανάθεσης στο PEP 572· το όνομα γίνεται μια τοπική μεταβλητή στο πλησιέστερο περιέχουν εύρος συνάρτησης, εκτός εάν υπάρχει μια εφαρμόσιμη δήλωση global ή nonlocal.
Με απλά λόγια, το NAME θα πετυχαίνει πάντα και θα ορίζει το NAME = <subject>.
8.6.4.5. Μοτίβα Μπαλαντέρ¶
Ένα μοτίβο μπαλαντέρ επιτυγχάνει πάντα (ταιριάζει με τα πάντα) και δεν δεσμεύει κανένα όνομα. Σύνταξη:
wildcard_pattern: '_'
Το _ είναι ένα soft keyword εντός οποιουδήποτε μοτίβου, αλλά μόνο εντός μοτίβων. Είναι ένα αναγνωριστικό, ως συνήθως, ακόμη και εντός των εκφράσεων θέματος στο match, στα guard, και στα μπλοκ case.
Με απλά λόγια, το _ θα επιτυγχάνει πάντα.
8.6.4.6. Μοτίβα Τιμής¶
Ένα μοτίβο τιμής αναπαριστά μια ονομασμένη τιμή στην Python. Σύνταξη:
value_pattern:attrattr:name_or_attr"." NAME name_or_attr:attr| NAME
Το όνομα με τελείες στο μοτίβο αναζητείται χρησιμοποιώντας τους τυπικούς κανόνες ανάλυσης ονομάτων της Python. Το πρότυπο επιτυγχάνει εάν η τιμή που βρέθηκε συγκρίνεται ως ίση με την τιμή του θέματος (χρησιμοποιώντας τον τελεστή ισότητας ==).
Με απλά λόγια, το NAME1.NAME2 θα πετύχει μόνο εάν <subject> == NAME1.NAME2
Σημείωση
Εάν η ίδια τιμή εμφανίζεται πολλές φορές στην ίδια δήλωση match, ο διερμηνέας μπορεί να αποθηκεύσει στην προσωρινή μνήμη την πρώτη τιμή που βρέθηκε και να την επαναχρησιμοποιήσει αντί να επαναλάβει την ίδια αναζήτηση. Αυτή η προσωρινή μνήμη είναι αυστηρά συνδεδεμένη με μια δεδομένη εκτέλεση μιας δεδομένης εντολής match.
8.6.4.7. Μοτίβα Ομαδοποίησης¶
Ένα μοτίβο ομαδοποίησης επιτρέπει στους χρήστες να προσθέτουν παρενθέσεις γύρω από τα μοτίβα για να τονίσουν την επιδιωκόμενη ομαδοποίηση. Διαφορετικά, δεν έχει πρόσθετη σύνταξη. Σύνταξη:
group_pattern: "(" pattern ")"
Με απλά λόγια, το (P) έχει το ίδιο αποτέλεσμα με το P.
8.6.4.8. Μοτίβα Ακολουθίας¶
Ένα μοτίβο ακολουθίας περιέχει πολλά υπο-πρότυπα που θα ταιριάξουν με τα στοιχεία μιας ακολουθίας. Η σύνταξη είναι παρόμοια με την αποσυμπίεση μιας λίστας ή μιας πλειάδας.
sequence_pattern: "[" [maybe_sequence_pattern] "]" | "(" [open_sequence_pattern] ")" open_sequence_pattern:maybe_star_pattern"," [maybe_sequence_pattern] maybe_sequence_pattern: ",".maybe_star_pattern+ ","? maybe_star_pattern:star_pattern|patternstar_pattern: "*" (capture_pattern|wildcard_pattern)
Δεν υπάρχει διαφορά, εάν χρησιμοποιούνται παρενθέσεις ή αγκύλες για τα μοτίβα ακολουθίας (δηλαδή (...) έναντι [...]).
Σημείωση
Ένα μεμονωμένο μοτίβο περικλειόμενο σε παρενθέσεις χωρίς τελικό κόμμα (π.χ. (3 | 4)) είναι ένα μοτίβο ομαδοποίησης. Ενώ ένα μεμονωμένο μοτίβο περικλειόμενο σε αγκύλες (π.χ. [3 | 4]) εξακολουθεί να είναι ένα μοτίβο ακολουθίας.
Μπορεί να υπάρχει το πολύ ένα υπο-μοτίβο με αστερίσκο σε ένα μοτίβο ακολουθίας. Το υπο-μοτίβο με αστερίσκο μπορεί να εμφανιστεί σε οποιαδήποτε θέση. Εάν δεν υπάρχει υπο-μοτίβο με αστερίσκο, το μοτίβο ακολουθίας είναι σταθερού μήκους· διαφορετικά είναι μεταβλητού μήκους.
Ακολουθεί η λογική ροή για το ταίριασμα ενός μοτίβου ακολουθίας έναντι μιας τιμής θέματος:
Εάν η τιμή του θέματος δεν είναι μια ακολουθία [2], το μοτίβο ακολουθίας αποτυγχάνει.
Εάν η τιμή του θέματος είναι στιγμιότυπο των
str,bytesήbytearrayτο μοτίβο ακολουθίας αποτυγχάνει.Τα επόμενα βήματα εξαρτώνται από το αν το μοτίβο ακολουθίας είναι σταθερού ή μεταβλητού μήκους.
Εάν το μοτίβο ακολουθίας είναι σταθερού μήκους:
Εάν το μήκος της ακολουθίας θέματος δεν είναι ίσο με τον αριθμό των υπο-μοτίβων, το μοτίβο ακολουθίας αποτυγχάνει
Τα υπο-μοτίβα στο μοτίβο ακολουθίας ταιριάζουν με τα αντίστοιχα στοιχεία τους στην ακολουθία θέματος από αριστερά προς τα δεξιά. Το ταίριασμα σταματά μόλις αποτύχει ένα υπο-μοτίβο. Εάν όλα τα υπο-μοτίβα επιτύχουν στο ταίριασμα του αντίστοιχου στοιχείου τους, το μοτίβο ακολουθίας επιτυγχάνει.
Διαφορετικά, εάν το μοτίβο ακολουθίας είναι μεταβλητού μήκους:
Εάν το μήκος της ακολουθίας θέματος είναι μικρότερο από τον αριθμό των υπο-μοτίβων χωρίς αστερίσκο, το μοτίβο ακολουθίας αποτυγχάνει.
Τα αρχικά υπο-μοτίβα χωρίς αστερίσκο ταιριάζουν με τα αντίστοιχα στοιχεία τους όπως και για τις ακολουθίες σταθερού μήκους.
Εάν το προηγούμενο βήμα επιτύχει, το υπο-μοτίβο με αστερίσκο ταιριάζει με μια λίστα που σχηματίζεται από τα εναπομείναντα στοιχεία του θέματος, εξαιρουμένων των στοιχείων που αντιστοιχούν σε υπο-μοτίβα χωρίς αστερίσκο τα οποία ακολουθούν το υπο-μοτίβο με αστερίσκο.
Τα εναπομείναντα υπο-μοτίβα χωρίς αστερίσκο ταιριάζουν με τα αντίστοιχα στοιχεία του θέματος, όπως και για μια ακολουθία σταθερού μήκους.
Σημείωση
Το μήκος της ακολουθίας θέματος λαμβάνεται μέσω της
len()(δηλαδή μέσω του πρωτοκόλλου__len__()). Αυτό το μήκος μπορεί να αποθηκευτεί στην προσωρινή μνήμη από τον διερμηνέα με παρόμοιο τρόπο όπως τα μοτίβα τιμής.
Με απλά λόγια, το [P1, P2, P3, … , P<N>] ταιριάζει μόνο εάν συμβούν όλα τα ακόλουθα:
έλεγχος ότι το
<subject>είναι μια ακολουθίαlen(subject) == <N>το
P1ταιριάζει στο<subject>[0](σημειώστε ότι αυτό το ταίριασμα μπορεί επίσης να δεσμεύσει ονόματα)το
P2ταιριάζει στο<subject>[1](σημειώστε ότι αυτό το ταίριασμα μπορεί επίσης να δεσμεύσει ονόματα)… και ούτω καθεξής για το αντίστοιχο ζεύγος μοτίβου/στοιχείου.
8.6.4.9. Μοτίβα Αντιστοίχισης¶
Ένα μοτίβο αντιστοίχισης περιέχει ένα ή περισσότερα μοτίβα κλειδιού-τιμής. Η σύνταξη είναι παρόμοια με την κατασκευή ενός λεξικού. Σύνταξη:
mapping_pattern: "{" [items_pattern] "}" items_pattern: ",".key_value_pattern+ ","? key_value_pattern: (literal_pattern|value_pattern) ":"pattern|double_star_patterndouble_star_pattern: "**"capture_pattern
Μπορεί να υπάρχει το πολύ ένα μοτίβο με διπλό αστερίσκο σε ένα μοτίβο αντιστοίχισης. Το μοτίβο με διπλό αστερίσκο πρέπει να είναι το τελευταίο υπο-μοτίβο στο μοτίβο αντιστοίχισης.
Δεν επιτρέπονται διπλότυπα κλειδιά σε μοτίβα αντιστοίχισης. Τα διπλότυπα λεκτικά κλειδιά θα κάνουν raise ένα SyntaxError. Δύο κλειδιά που κατά τα άλλα έχουν την ίδια τιμή θα κάνουν raise ένα ValueError κατά τον χρόνο εκτέλεσης.
Ακολουθεί η λογική ροή για το ταίριασμα ενός μοτίβου αντιστοίχισης έναντι μιας τιμής θέματος:
Εάν η τιμή του θέματος δεν είναι μία αντιστοίχιση [3], το μοτίβο αντιστοίχισης αποτυγχάνει.
Εάν κάθε κλειδί που δίνεται στο μοτίβο αντιστοίχισης είναι παρόν στην αντιστοίχιση του θέματος, και το μοτίβο για κάθε κλειδί ταιριάζει με το αντίστοιχο στοιχείο της αντιστοίχισης του θέματος, το μοτίβο αντιστοίχισης επιτυγχάνει.
Εάν ανιχνευτούν διπλότυπα κλειδιά στο μοτίβο αντιστοίχισης, το μοτίβο θεωρείται μη έγκυρο. Γίνεται raise ένα
SyntaxErrorγια διπλότυπες λεκτικές τιμές· ή έναValueErrorγια ονομασμένα κλειδιά με την ίδια τιμή.
Σημείωση
Τα ζεύγη κλειδιού-τιμής ταιριάζονται χρησιμοποιώντας τη μορφή των δύο ορισμάτων της μεθόδου get() της αντιστοίχισης του θέματος. Τα ταιριασμένα ζεύγη κλειδιού-τιμής πρέπει να είναι ήδη παρόντα στην αντιστοίχιση, και να μην δημιουργούνται δυναμικά μέσω της __missing__() ή της __getitem__().
Με απλά λόγια, το {KEY1: P1, KEY2: P2, ... } ταιριάζει μόνο εάν συμβούν όλα τα ακόλουθα:
έλεγχος ότι το
<subject>είναι μία αντιστοίχισηKEY1 in <subject>το
P1ταιριάζει στο<subject>[KEY1]… και ούτω καθεξής για το αντίστοιχο ζεύγος KEY/μοτίβου.
8.6.4.10. Μοτίβα Κλάσης¶
Ένα μοτίβο κλάσης αναπαριστά μια κλάση και τα ορίσματα θέσης και λέξης κλειδιού της (εάν υπάρχουν). Σύνταξη:
class_pattern:name_or_attr"(" [pattern_arguments","?] ")" pattern_arguments:positional_patterns[","keyword_patterns] |keyword_patternspositional_patterns: ",".pattern+ keyword_patterns: ",".keyword_pattern+ keyword_pattern: NAME "="pattern
Η ίδια λέξη-κλειδί δεν πρέπει να επαναλαμβάνεται στα μοτίβα κλάσης.
Ακολουθεί η λογική ροή για το ταίριασμα ενός μοτίβου κλάσης έναντι μιας τιμής θέματος:
Εάν το
name_or_attrδεν είναι στιγμιότυπο του ενσωματωμένουtype, γίνεται raise έναTypeError.Εάν η τιμή του θέματος δεν είναι στιγμιότυπο του
name_or_attr(ελέγχεται μέσω τηςisinstance()), το μοτίβο κλάσης αποτυγχάνει.Εάν δεν υπάρχουν ορίσματα μοτίβου, το μοτίβο επιτυγχάνει. Διαφορετικά, τα επόμενα βήματα εξαρτώνται από το αν υπάρχουν μοτίβα λέξης-κλειδιού ή ορισμάτων θέσης.
Για έναν αριθμό ενσωματωμένων τύπων (καθορίζονται παρακάτω), γίνεται αποδεκτό ένα μεμονωμένο υπο-μοτίβο θέσης το οποίο θα ταιριάξει με ολόκληρο το θέμα· για αυτούς τους τύπους, τα μοτίβα λέξης-κλειδιού λειτουργούν επίσης όπως και για άλλους τύπους.
Εάν υπάρχουν μόνο μοτίβα λέξης-κλειδιού, επεξεργάζονται ως εξής, ένα προς ένα:
Η λέξη-κλειδί αναζητείται ως χαρακτηριστικό πάνω στο θέμα.
Εάν κάνει raise μια εξαίρεση διαφορετική από το
AttributeError, η εξαίρεση ανεβαίνει προς τα πάνω.Εάν κάνει raise το
AttributeError, το μοτίβο κλάσης έχει αποτύχει.Αλλιώς, το υπο-μοτίβο που σχετίζεται με το μοτίβο λέξης-κλειδιού ταιριάζεται με την τιμή του χαρακτηριστικού του θέματος. Εάν αυτό αποτύχει, το μοτίβο κλάσης αποτυγχάνει· εάν επιτύχει, το ταίριασμα προχωρά στην επόμενη λέξη-κλειδί.
Εάν όλα τα μοτίβα λέξης-κλειδιού επιτύχουν, το μοτίβο κλάσης επιτυγχάνει.
Εάν υπάρχουν οποιαδήποτε μοτίβα θέσης, μετατρέπονται σε μοτίβα λέξης-κλειδιού χρησιμοποιώντας το χαρακτηριστικό
__match_args__πάνω στην κλάσηname_or_attrπριν από το ταίριασμα:Καλείται το ισοδύναμο της
getattr(cls, "__match_args__", ()).Εάν κάνει raise μια εξαίρεση, η εξαίρεση ανεβαίνει προς τα πάνω.
Εάν η επιστρεφόμενη τιμή δεν είναι πλειάδα, η μετατροπή αποτυγχάνει και γίνεται raise το
TypeError.Εάν υπάρχουν περισσότερα μοτίβα θέσης από το
len(cls.__match_args__), γίνεται raise τοTypeError.Διαφορετικά, το μοτίβο θέσης
iμετατρέπεται σε ένα μοτίβο λέξης-κλειδιού χρησιμοποιώντας το__match_args__[i]ως λέξη-κλειδί. Το__match_args__[i]πρέπει να είναι συμβολοσειρά· εάν όχι, γίνεται raise τοTypeError.Εάν υπάρχουν διπλότυπες λέξεις-κλειδιά, γίνεται raise το
TypeError.
Μόλις όλα τα μοτίβα θέσης μετατραπούν σε μοτίβα λέξης-κλειδιού, το ταίριασμα προχωρά σαν να υπήρχαν μόνο μοτίβα λέξης-κλειδιού.
Για τους ακόλουθους ενσωματωμένους τύπους, ο χειρισμός των υπο-μοτίβων θέσης είναι διαφορετικός:
Αυτές οι κλάσεις δέχονται ένα μόνο όρισμα θέσης, και το μοτίβο εκεί ταιριάζεται έναντι ολόκληρου του αντικειμένου αντί για ένα χαρακτηριστικό. Για παράδειγμα το
int(0|1)ταιριάζει με την τιμή0, αλλά όχι με την τιμή0.0.
Με απλά λόγια, το CLS(P1, attr=P2) ταιριάζει μόνο εάν συμβαίνουν τα ακόλουθα:
isinstance(<subject>, CLS)μετατροπή του
P1σε μοτίβο λέξης-κλειδιού χρησιμοποιώντας τοCLS.__match_args__Για κάθε όρισμα λέξης-κλειδιού
attr=P2:hasattr(<subject>, "attr")το
P2ταιριάζει στο<subject>.attr
… και ούτω καθεξής για το αντίστοιχο ζεύγος ορίσματος λέξης-κλειδιού/μοτίβου.
8.7. Ορισμοί συναρτήσεων¶
Ένας ορισμός συνάρτησης ορίζει ένα αντικείμενο συνάρτησης ορισμένο από τον χρήστη (δείτε την ενότητα Η τυπική ιεραρχία τύπου):
funcdef: [decorators] "def"funcname[type_params] "(" [parameter_list] ")" ["->"expression] ":"suitedecorators:decorator+ decorator: "@"assignment_expressionNEWLINE parameter_list:defparameter(","defparameter)* "," "/" ["," [parameter_list_no_posonly]] |parameter_list_no_posonlyparameter_list_no_posonly:defparameter(","defparameter)* ["," [parameter_list_starargs]] |parameter_list_starargsparameter_list_starargs: "*" [star_parameter] (","defparameter)* ["," [parameter_star_kwargs]] | "*" (","defparameter)+ ["," [parameter_star_kwargs]] |parameter_star_kwargsparameter_star_kwargs: "**"parameter[","] parameter:identifier[":"expression] star_parameter:identifier[":" ["*"]expression] defparameter:parameter["="expression] funcname:identifier
Ο ορισμός μιας συνάρτησης είναι μια εκτελέσιμη δήλωση. Η εκτέλεσή της δεσμεύει το όνομα της συνάρτησης στον τρέχοντα τοπικό χώρο ονομάτων σε ένα αντικείμενο συνάρτησης (ένας wrapper γύρω από τον εκτελέσιμο κώδικα της συνάρτησης). Αυτό το αντικείμενο συνάρτησης περιέχει μια αναφορά στον τρέχοντα καθολικό χώρο ονομάτων, ως τον καθολικό χώρο ονομάτων που θα χρησιμοποιηθεί όταν κληθεί η συνάρτηση.
Ο ορισμός της συνάρτησης δεν εκτελεί το σώμα της συνάρτησης· αυτό εκτελείται μόνο όταν κληθεί η συνάρτηση. [4]
Ένας ορισμός συνάρτησης μπορεί να περιτυλιχθεί από μία ή περισσότερες εκφράσεις decorator. Οι εκφράσεις decorator αξιολογούνται όταν ορίζεται η συνάρτηση, στο εύρος που περιέχει τον ορισμό της συνάρτησης. Το αποτέλεσμα πρέπει να είναι ένα callable, το οποίο καλείται με το αντικείμενο συνάρτησης ως το μοναδικό όρισμα. Η τιμή που επιστρέφεται δεσμεύεται στο όνομα της συνάρτησης αντί για το αντικείμενο συνάρτησης. Πολλοί decorators εφαρμόζονται με εμφωλευμένο τρόπο. Για παράδειγμα, ο ακόλουθος κώδικας
@f1(arg)
@f2
def func(): pass
είναι κατά προσέγγιση ισοδύναμο με το
def func(): pass
func = f1(arg)(f2(func))
εκτός του ότι η αρχική συνάρτηση δεν δεσμεύεται προσωρινά στο όνομα func.
Άλλαξε στην έκδοση 3.9: Οι συναρτήσεις μπορούν να διακοσμηθούν με οποιοδήποτε έγκυρο assignment_expression. Προηγουμένως, η γραμματική ήταν πολύ πιο αυστηρή· δείτε το PEP 614 για λεπτομέρειες.
Μια λίστα από παραμέτρους τύπου μπορεί να δοθεί μέσα σε αγκύλες μεταξύ του ονόματος της συνάρτησης και της παρένθεσης ανοίγματος για τη λίστα παραμέτρων της. Αυτό υποδεικνύει στους ελέγχους στατικού τύπου ότι η συνάρτηση είναι γενική. Κατά τον χρόνο εκτέλεσης, οι παράμετροι τύπου μπορούν να ανακτηθούν από το χαρακτηριστικό __type_params__ της συνάρτησης. Δείτε το Γενικές συναρτήσεις για περισσότερα.
Άλλαξε στην έκδοση 3.12: Οι λίστες παραμέτρων τύπου είναι νέες στην Python 3.12.
Όταν μία ή περισσότερες παράμετροι έχουν τη μορφή parameter = expression, η συνάρτηση λέγεται ότι έχει «προεπιλεγμένες τιμές παραμέτρων.» Για μια παράμετρο με προεπιλεγμένη τιμή, το αντίστοιχο argument μπορεί να παραλειφθεί από μια κλήση, οπότε αντικαθίσταται η προεπιλεγμένη τιμή της παραμέτρου. Εάν μια παράμετρος έχει μια προεπιλεγμένη τιμή, όλες οι επόμενες παράμετροι μέχρι το «*» πρέπει επίσης να έχουν μια προεπιλεγμένη τιμή — αυτός είναι ένας συντακτικός περιορισμός που δεν εκφράζεται από τη γραμματική.
Οι προεπιλεγμένες τιμές παραμέτρων αξιολογούνται από αριστερά προς τα δεξιά όταν εκτελείται ο ορισμός της συνάρτησης. Αυτό σημαίνει ότι η έκφραση αξιολογείται μία φορά, όταν ορίζεται η συνάρτηση, και χρησιμοποιείται η ίδια «προ-υπολογισμένη» τιμή για κάθε κλήση. Αυτό είναι ιδιαίτερα σημαντικό να κατανοηθεί, όταν μια προεπιλεγμένη τιμή παραμέτρου είναι ένα μεταβλητό αντικείμενο, όπως μια λίστα ή ένα λεξικό: εάν η συνάρτηση τροποποιήσει το αντικείμενο (π.χ. προσθέτοντας ένα στοιχείο σε μια λίστα), η προεπιλεγμένη τιμή παραμέτρου τροποποιείται στην πράξη. Αυτό συνήθως δεν είναι αυτό που επιδιωκόταν. Ένας τρόπος παράκαμψης είναι να χρησιμοποιήσετε το None ως προεπιλογή, και να το ελέγχετε ρητά στο σώμα της συνάρτησης, π.χ.:
def whats_on_the_telly(penguin=None):
if penguin is None:
penguin = []
penguin.append("property of the zoo")
return penguin
Η σημασιολογία των κλήσεων συναρτήσεων περιγράφεται λεπτομερέστερα στην ενότητα Κλήσεις. Μια κλήση συνάρτησης αναθέτει πάντα τιμές σε όλες τις παραμέτρους που αναφέρονται στη λίστα παραμέτρων, είτε από ορίσματα θέσης, από ορίσματα λέξης-κλειδιού, είτε από προεπιλεγμένες τιμές. Εάν η μορφή «*identifier» υπάρχει, αρχικοποιείται σε μία πλειάδα που λαμβάνει τυχόν επιπλέον παραμέτρους θέσης, έχοντας ως προεπιλογή την κενή πλειάδα. Εάν η μορφή «**identifier» υπάρχει, αρχικοποιείται σε μία νέα ταξινομημένη αντιστοίχιση που λαμβάνει τυχόν επιπλέον ορίσματα λέξης-κλειδιού, έχοντας ως προεπιλογή μία νέα κενή αντιστοίχιση του ίδιου τύπου. Οι παράμετροι μετά το «*» ή το «*identifier» είναι μόνο παράμετροι λέξης-κλειδιού και μπορούν να περαστούν μόνο από ορίσματα λέξης-κλειδιού. Οι παράμετροι πριν από το «/» είναι μόνο παράμετροι θέσης και μπορούν να περαστούν μόνο από ορίσματα θέσης.
Άλλαξε στην έκδοση 3.8: Η σύνταξη παραμέτρων συνάρτησης / μπορεί να χρησιμοποιηθεί για να υποδείξει μόνο παραμέτρους θέσης. Δείτε το PEP 570 για λεπτομέρειες.
Οι παράμετροι μπορεί να έχουν ένα annotation της μορφής «: expression» μετά το όνομα της παραμέτρου. Οποιαδήποτε παράμετρος μπορεί να έχει ένα annotation, ακόμα και αυτές της μορφής *identifier ή **identifier. (Ως ειδική περίπτωση, οι παράμετροι της μορφής *identifier μπορεί να έχουν ένα annotation «: *expression».) Οι συναρτήσεις μπορεί να έχουν «return» annotation της μορφής «-> expression» μετά τη λίστα παραμέτρων. Αυτά τα annotations μπορούν να είναι οποιαδήποτε έγκυρη έκφραση της Python. Η παρουσία των annotations δεν αλλάζει τη σημασιολογία μιας συνάρτησης. Δείτε το Annotations για περισσότερες πληροφορίες.
Άλλαξε στην έκδοση 3.11: Οι παράμετροι της μορφής «*identifier» μπορεί να έχουν ένα annotation «: *expression». Δείτε το PEP 646.
Είναι επίσης δυνατό να δημιουργηθούν ανώνυμες συναρτήσεις (συναρτήσεις που δεν δεσμεύονται σε ένα όνομα), για άμεση χρήση σε εκφράσεις. Αυτό χρησιμοποιεί εκφράσεις lambda, που περιγράφονται στην ενότητα Εκφράσεις lambda. Σημειώστε ότι η έκφραση lambda είναι απλώς μια συντομογραφία για έναν απλοποιημένο ορισμό συνάρτησης· μια συνάρτηση που ορίζεται με την δήλωση «def» μπορεί να περαστεί ή να ανατεθεί σε ένα άλλο όνομα ακριβώς όπως μια συνάρτηση που ορίζεται με μια έκφραση lambda. Η μορφή «def» είναι στην πραγματικότητα πιο ισχυρή αφού επιτρέπει την εκτέλεση πολλαπλών εντολών και annotations.
Σημείωση για προγραμματιστές/τριες: Οι συναρτήσεις είναι first-class αντικείμενα. Μια δήλωση «def» που εκτελείται μέσα σε έναν ορισμό συνάρτησης ορίζει μια τοπική συνάρτηση που μπορεί να επιστραφεί ή να περαστεί αλλού. Οι ελεύθερες μεταβλητές που χρησιμοποιούνται στην εμφωλευμένη συνάρτηση έχουν πρόσβαση στις τοπικές μεταβλητές της συνάρτησης που περιέχει το def. Δείτε την ενότητα Ονομασία και σύνδεση για λεπτομέρειες.
Δείτε επίσης
- PEP 3107 - Annotations Συνάρτησης
Οι αρχικές προδιαγραφές για τα annotations συνάρτησης.
- PEP 484 - Υποδείξεις Τύπου
Ορισμός ενός τυποποιημένου νοήματος για τα annotations: υποδείξεις τύπου.
- PEP 526 - Σύνταξη για Annotations Μεταβλητών
Δυνατότητα για υποδείξεις τύπου στις δηλώσεις μεταβλητών, συμπεριλαμβανομένων των μεταβλητών κλάσης και των μεταβλητών στιγμιοτύπου.
- PEP 563 - Αναβαλλόμενη Αξιολόγηση των Annotations
Υποστήριξη για μελλοντική αναφορά μέσα στα annotations διατηρώντας τα annotations σε μορφή συμβολοσειράς κατά τον χρόνο εκτέλεσης αντί για άμεση αξιολόγηση.
- PEP 318 - Decorators για Συναρτήσεις και Μεθόδους
Εισήχθησαν οι decorators συναρτήσεων και μεθόδων. Οι decorators κλάσεων εισήχθησαν στο PEP 3129.
8.8. Ορισμοί κλάσεων¶
Ο ορισμός μιας κλάσης ορίζει ένα αντικείμενο κλάσης (δείτε την ενότητα Η τυπική ιεραρχία τύπου):
classdef: [decorators] "class"classname[type_params] [inheritance] ":"suiteinheritance: "(" [argument_list] ")" classname:identifier
Ένας ορισμός κλάσης είναι μια εκτελέσιμη δήλωση. Η λίστα κληρονομικότητας συνήθως δίνει μια λίστα βασικών κλάσεων (δείτε το Μετα-κλάσεις για πιο προχωρημένες χρήσεις), επομένως κάθε στοιχείο στη λίστα θα πρέπει να αξιολογείται σε ένα αντικείμενο κλάσης που επιτρέπει τη δημιουργία υπο-κλάσεων. Οι κλάσεις χωρίς λίστα κληρονομικότητας κληρονομούν, εξ ορισμού, από τη βασική κλάση object· επομένως, το
class Foo:
pass
είναι ισοδύναμο με το
class Foo(object):
pass
There may be one or more base classes; see Multiple inheritance below for more information.
Η σουίτα της κλάσης στη συνέχεια εκτελείται σε ένα νέο πλαίσιο εκτέλεσης (δείτε το Ονομασία και σύνδεση), χρησιμοποιώντας έναν νεοδημιουργηθέντα τοπικό χώρο ονομάτων και τον αρχικό καθολικό χώρο ονομάτων. (Συνήθως, η σουίτα περιέχει κυρίως ορισμούς συναρτήσεων.) Όταν η σουίτα της κλάσης ολοκληρώσει την εκτέλεσή της, το πλαίσιο εκτέλεσής της απορρίπτεται αλλά ο τοπικός χώρος ονομάτων της σώζεται. [5] Ένα αντικείμενο κλάσης δημιουργείται στη συνέχεια χρησιμοποιώντας τη λίστα κληρονομικότητας για τις βασικές κλάσεις και τον αποθηκευμένο τοπικό χώρο ονομάτων για το λεξικό των χαρακτηριστικών. Το όνομα της κλάσης δεσμεύεται σε αυτό το αντικείμενο κλάσης στον αρχικό τοπικό χώρο ονομάτων.
Η σειρά με την οποία ορίζονται τα χαρακτηριστικά στο σώμα της κλάσης διατηρείται στο __dict__ της νέας κλάσης. Σημειώστε ότι αυτή είναι αξιόπιστη μόνο αμέσως μετά τη δημιουργία της κλάσης και μόνο για κλάσεις που ορίστηκαν χρησιμοποιώντας τη σύνταξη ορισμού.
Η δημιουργία κλάσεων μπορεί να προσαρμοστεί εκτενώς χρησιμοποιώντας metaclasses.
Οι κλάσεις μπορούν επίσης να διακοσμηθούν: ακριβώς όπως με τους decorators συναρτήσεων,
@f1(arg)
@f2
class Foo: pass
είναι κατά προσέγγιση ισοδύναμο με το
class Foo: pass
Foo = f1(arg)(f2(Foo))
Οι κανόνες αξιολόγησης για τις εκφράσεις decorators είναι οι ίδιοι με αυτούς των decorators συναρτήσεων. Το αποτέλεσμα στη συνέχεια δεσμεύεται στο όνομα της κλάσης.
Άλλαξε στην έκδοση 3.9: Οι κλάσεις μπορούν να διακοσμηθούν με οποιοδήποτε έγκυρο assignment_expression. Προηγουμένως, η γραμματική ήταν πολύ πιο αυστηρή· δείτε το PEP 614 για λεπτομέρειες.
Μια λίστα από παραμέτρους τύπου μπορεί να δοθεί σε αγκύλες αμέσως μετά το όνομα της κλάσης. Αυτό υποδεικνύει στους ελέγχους στατικού τύπου ότι η κλάση είναι γενική. Κατά τον χρόνο εκτέλεσης, οι παράμετροι τύπου μπορούν να ανακτηθούν από το χαρακτηριστικό __type_params__ της κλάσης. Δείτε το Γενικές κλάσεις για περισσότερα.
Άλλαξε στην έκδοση 3.12: Οι λίστες παραμέτρων τύπου είναι νέες στην Python 3.12.
Σημείωση για προγραμματιστές/τριες: Οι μεταβλητές που ορίζονται στον ορισμό της κλάσης είναι χαρακτηριστικά κλάσης· μοιράζονται μεταξύ των στιγμιοτύπων. Τα χαρακτηριστικά στιγμιοτύπου μπορούν να οριστούν σε μια μέθοδο με self.name = value. Τόσο χαρακτηριστικά κλάσης όσο και τα χαρακτηριστικά στιγμιοτύπου είναι προσβάσιμα μέσω της σημειογραφίας «self.name», και ένα χαρακτηριστικό στιγμιοτύπου κρύβει ένα χαρακτηριστικό κλάσης με το ίδιο όνομα όταν η πρόσβαση γίνεται με αυτόν τον τρόπο. Τα χαρακτηριστικά κλάσης μπορούν να χρησιμοποιηθούν ως προεπιλογές για τα χαρακτηριστικά στιγμιοτύπου, αλλά η χρήση μεταβλητών τιμών μπορεί να οδηγήσει σε μη αναμενόμενα αποτελέσματα. Τα Descriptors μπορούν να χρησιμοποιηθούν για τη δημιουργία μεταβλητών στιγμιοτύπου με διαφορετικές λεπτομέρειες υλοποίησης.
Δείτε επίσης
- PEP 3115 - Metaclasses στην Python 3000
Η πρόταση που άλλαξε τη δήλωση των metaclasses στην τρέχουσα σύνταξη, και τη σημασιολογία για τον τρόπο κατασκευής κλάσεων με metaclasses.
- PEP 3129 - Decorators Κλάσεων
Η πρόταση που πρόσθεσε τους decorators κλάσεων. Οι decorators συναρτήσεων και μεθόδων είχαν εισαχθεί στο PEP 318.
8.8.1. Multiple inheritance¶
Python classes may have multiple base classes, a technique known as multiple inheritance. The base classes are specified in the class definition by listing them in parentheses after the class name, separated by commas. For example, the following class definition:
>>> class A: pass
>>> class B: pass
>>> class C(A, B): pass
defines a class C that inherits from classes A and B.
The method resolution order (MRO) is the order in which base classes are searched when looking up an attribute on a class. See The Python 2.3 Method Resolution Order for a description of how Python determines the MRO for a class.
Multiple inheritance is not always allowed. Attempting to define a class with multiple inheritance will raise an error if one of the bases does not allow subclassing, if a consistent MRO cannot be created, if no valid metaclass can be determined, or if there is an instance layout conflict. We’ll discuss each of these in turn.
First, all base classes must allow subclassing. While most classes allow subclassing,
some built-in classes do not, such as bool:
>>> class SubBool(bool): # TypeError
... pass
Traceback (most recent call last):
...
TypeError: type 'bool' is not an acceptable base type
In the resolved MRO of a class, the class’s bases appear in the order they were specified in the class’s bases list. Additionally, the MRO always lists a child class before any of its bases. A class definition will fail if it is impossible to resolve a consistent MRO that satisfies these rules from the list of bases provided:
>>> class Base: pass
>>> class Child(Base): pass
>>> class Grandchild(Base, Child): pass # TypeError
Traceback (most recent call last):
...
TypeError: Cannot create a consistent method resolution order (MRO) for bases Base, Child
In the MRO of Grandchild, Base must appear before Child because it is first
in the base class list, but it must also appear after Child because it is a parent of
Child. This is a contradiction, so the class cannot be defined.
If some of the bases have a custom metaclass, the metaclass of the resulting class is chosen among the metaclasses of the bases and the explicitly specified metaclass of the child class. It must be a metaclass that is a subclass of all other candidate metaclasses. If no such metaclass exists among the candidates, the class cannot be created, as explained in Προσδιορισμός της κατάλληλης μετακλάσης.
Finally, the instance layouts of the bases must be compatible. This means that it must be possible to compute a solid base for the class. Exactly which classes are solid bases depends on the Python implementation.
Λεπτομέρεια υλοποίησης CPython: In CPython, a class is a solid base if it has a
nonempty __slots__ definition.
Many but not all classes defined in C are also solid bases, including most
builtins (such as int or BaseException)
but excluding most concrete Exception classes. Generally, a C class
is a solid base if its underlying struct is different in size from its base class.
Every class has a solid base. object, the base class, has itself as its solid base.
If there is a single base, the child class’s solid base is that class if it is a solid base,
or else the base class’s solid base. If there are multiple bases, we first find the solid base
for each base class to produce a list of candidate solid bases. If there is a unique solid base
that is a subclass of all others, then that class is the solid base. Otherwise, class creation
fails.
Example:
>>> class Solid1:
... __slots__ = ("solid1",)
>>>
>>> class Solid2:
... __slots__ = ("solid2",)
>>>
>>> class SolidChild(Solid1):
... __slots__ = ("solid_child",)
>>>
>>> class C1: # solid base is `object`
... pass
>>>
>>> # OK: solid bases are `Solid1` and `object`, and `Solid1` is a subclass of `object`.
>>> class C2(Solid1, C1): # solid base is `Solid1`
... pass
>>>
>>> # OK: solid bases are `SolidChild` and `Solid1`, and `SolidChild` is a subclass of `Solid1`.
>>> class C3(SolidChild, Solid1): # solid base is `SolidChild`
... pass
>>>
>>> # Error: solid bases are `Solid1` and `Solid2`, but neither is a subclass of the other.
>>> class C4(Solid1, Solid2): # error: no single solid base
... pass
Traceback (most recent call last):
...
TypeError: multiple bases have instance lay-out conflict
8.9. Coroutines¶
Added in version 3.5.
8.9.1. Ορισμός συνάρτησης coroutine¶
async_funcdef: [decorators] "async" "def"funcname"(" [parameter_list] ")" ["->"expression] ":"suite
Η εκτέλεση των Python coroutines μπορεί να ανασταλεί και να συνεχιστεί σε πολλά σημεία (δείτε το coroutine). Οι εκφράσεις await, το async for και το async with μπορούν να χρησιμοποιηθούν μόνο στο σώμα μιας συνάρτησης coroutine.
Οι συναρτήσεις που ορίζονται με τη σύνταξη async def είναι πάντα συναρτήσεις coroutine, ακόμη και αν δεν περιέχουν τις λέξεις-κλειδιά await ή async.
Προκαλείται SyntaxError αν χρησιμοποιηθεί μια έκφραση yield from μέσα στο σώμα μιας συνάρτησης coroutine.
Ένα παράδειγμα συνάρτησης coroutine:
async def func(param1, param2):
do_stuff()
await some_coroutine()
Άλλαξε στην έκδοση 3.7: Το await και το async είναι πλέον λέξεις-κλειδιά· προηγουμένως αντιμετωπίζονταν ως τέτοια μόνο εντός του σώματος μιας συνάρτησης coroutine.
8.9.2. Η δήλωση async for¶
async_for_stmt: "async" for_stmt
Ένα asynchronous iterable παρέχει μια μέθοδο __aiter__ η οποία επιστρέφει απευθείας έναν asynchronous iterator, ο οποίος μπορεί να καλέσει ασύγχρονο κώδικα στη μέθοδο __anext__ του.
Η δήλωση async for επιτρέπει τη βολική επανάληψη σε ασύγχρονα iterables.
Ο ακόλουθος κώδικας:
async for TARGET in ITER:
SUITE
else:
SUITE2
Είναι σημασιολογικά ισοδύναμο με το:
iter = (ITER).__aiter__()
running = True
while running:
try:
TARGET = await iter.__anext__()
except StopAsyncIteration:
running = False
else:
SUITE
else:
SUITE2
except that implicit special method lookup is used
for __aiter__() and __anext__().
Προκαλείται SyntaxError αν χρησιμοποιηθεί μια δήλωση async for έξω από το σώμα μιας συνάρτησης coroutine.
8.9.3. Η δήλωση async with¶
async_with_stmt: "async" with_stmt
Ένας asynchronous context manager είναι ένας context manager που είναι ικανός να αναστείλει την εκτέλεση στις μεθόδους του enter και exit.
Ο ακόλουθος κώδικας:
async with EXPRESSION as TARGET:
SUITE
είναι σημασιολογικά ισοδύναμος με:
manager = (EXPRESSION)
aenter = manager.__aenter__
aexit = manager.__aexit__
value = await aenter()
hit_except = False
try:
TARGET = value
SUITE
except:
hit_except = True
if not await aexit(*sys.exc_info()):
raise
finally:
if not hit_except:
await aexit(None, None, None)
except that implicit special method lookup is used
for __aenter__() and __aexit__().
Προκαλείται SyntaxError αν χρησιμοποιηθεί μια δήλωση async with έξω από το σώμα μιας συνάρτησης coroutine.
Δείτε επίσης
- PEP 492 - Coroutines με σύνταξη async και await
Η πρόταση που έκανε τις coroutines μια σωστή, ανεξάρτητη έννοια στην Python, και πρόσθεσε την υποστηρικτική σύνταξη.
8.10. Λίστες παραμέτρων τύπου¶
Added in version 3.12.
Άλλαξε στην έκδοση 3.13: Προστέθηκε υποστήριξη για προεπιλεγμένες τιμές (δείτε το PEP 696).
type_params: "["type_param(","type_param)* "]" type_param:typevar|typevartuple|paramspectypevar:identifier(":"expression)? ("="expression)? typevartuple: "*"identifier("="expression)? paramspec: "**"identifier("="expression)?
Οι Συναρτήσεις (συμπεριλαμβανομένων των coroutines), οι κλάσεις και τα ψευδώνυμα τύπων μπορούν να περιέχουν μια λίστα παραμέτρων τύπου:
def max[T](args: list[T]) -> T:
...
async def amax[T](args: list[T]) -> T:
...
class Bag[T]:
def __iter__(self) -> Iterator[T]:
...
def add(self, arg: T) -> None:
...
type ListOrSet[T] = list[T] | set[T]
Σημασιολογικά, αυτό υποδηλώνει ότι η συνάρτηση, η κλάση ή το ψευδώνυμο τύπου είναι γενικά ως προς μια μεταβλητή τύπου. Αυτή η πληροφορία χρησιμοποιείται κυρίως από τους ελεγκτές στατικού τύπου, και κατά τον χρόνο εκτέλεσης, τα γενικά αντικείμενα συμπεριφέρονται σχεδόν όπως τα αντίστοιχα μη γενικά.
Οι παράμετροι τύπου δηλώνονται μέσα σε αγκύλες ([]) αμέσως μετά το όνομα της συνάρτησης, κλάσης ή του ψευδωνύμου τύπου. Οι παράμετροι τύπου είναι προσβάσιμες εντός της εμβέλειας του γενικού αντικειμένου, αλλά όχι αλλού. Επομένως, μετά από μια δήλωση def func[T](): pass, το όνομα T δεν είναι διαθέσιμο στο εύρος του module. Παρακάτω, η σημασιολογία των γενικών αντικειμένων περιγράφεται με μεγαλύτερη ακρίβεια. Το εύρος των παραμέτρων τύπου μοντελοποιείται με μια ειδική συνάρτηση (τεχνικά, ένα annotation scope) η οποία περιτυλίγει τη δημιουργία του γενικού αντικειμένου.
Οι γενικές συναρτήσεις, κλάσεις και τα ψευδώνυμα τύπων έχουν ένα χαρακτηριστικό __type_params__ που παραθέτει τις παραμέτρους του τύπου τους.
Οι παράμετροι τύπου διακρίνονται σε τρία είδη:
Το
typing.TypeVar, που εισάγεται από ένα απλό όνομα (π.χ.T). Σημασιολογικά, αναπαριστά έναν μόνο τύπο σε έναν ελεγκτή τύπου.Το
typing.TypeVarTuple, που εισάγεται από ένα όνομα με πρόθεμα έναν απλό αστερίσκο (π.χ.*Ts). Σημασιολογικά, αντιπροσωπεύει μία πλειάδα οποιουδήποτε αριθμού τύπων.Το
typing.ParamSpec, που εισάγεται από ένα όνομα με πρόθεμα δύο αστερίσκους (π.χ.**P). Σημασιολογικά, αντιπροσωπεύει τις παραμέτρους ενός callable.
Οι δηλώσεις του typing.TypeVar μπορούν να ορίσουν bounds και constraints με άνω και κάτω τελεία (:) ακολουθούμενη από μια έκφραση. Μία μεμονωμένη έκφραση μετά την άνω και κάτω τελεία υποδεικνύει ένα bound (π.χ. T: int). Σημασιολογικά, αυτό σημαίνει ότι το typing.TypeVar μπορεί να αναπαριστά μόνο τύπους που είναι υπότυπος αυτού του bound. Μία παρενθετική πλειάδα εκφράσεων μετά την άνω και κάτω τελεία υποδεικνύει ένα σύνολο από constraints (π.χ. T: (str, bytes)). Κάθε μέλος της πλειάδας θα πρέπει να είναι ένας τύπος (και πάλι, αυτό δεν επιβάλλεται κατά τον χρόνο εκτέλεσης). Οι constraint μεταβλητές τύπου μπορούν να λάβουν μόνο έναν από τους τύπους στη λίστα των constraints.
Για τα typing.TypeVar που δηλώνονται χρησιμοποιώντας τη σύνταξη λίστας παραμέτρων τύπου, τα bound και constraints δεν αξιολογούνται όταν το γενικό αντικείμενο δημιουργείται, παρά μόνο όταν πραγματοποιείται ρητή πρόσβαση στην τιμή μέσω των χαρακτηριστικών __bound__ και __constraints__. Για να επιτευχθεί αυτό, τα bounds ή constraints αξιολογούνται σε ένα ξεχωριστό annotation scope.
Τα typing.TypeVarTuple και τα typing.ParamSpec δεν μπορούν να έχουν bounds ή constraints.
Και τα τρία είδη παραμέτρων τύπου μπορούν επίσης να έχουν μια προεπιλεγμένη τιμή, η οποία χρησιμοποιείται όταν η παράμετρος τύπου δεν παρέχεται ρητά. Αυτό προστίθεται προσαρτώντας ένα απλό σύμβολο ίσον (=) ακολουθούμενο από μια έκφραση. Όπως τα bounds και τα constraints των μεταβλητών τύπου, η προεπιλεγμένη τιμή δεν αξιολογείται κατά τη δημιουργία του αντικειμένου, παρά μόνο κατά την πρόσβαση στο χαρακτηριστικό __default__ της παραμέτρου τύπου. Για τον σκοπό αυτό, η προεπιλεγμένη τιμή αξιολογείται σε ένα ξεχωριστό annotation scope. Εάν δεν καθοριστεί προεπιλεγμένη τιμή για μια παράμετρο τύπου, το χαρακτηριστικό __default__ ορίζεται στο ειδικό αντικείμενο φρουρό typing.NoDefault.
Το ακόλουθο παράδειγμα υποδεικνύει το πλήρες σύνολο των επιτρεπόμενων δηλώσεων παραμέτρων τύπου:
def overly_generic[
SimpleTypeVar,
TypeVarWithDefault = int,
TypeVarWithBound: int,
TypeVarWithConstraints: (str, bytes),
*SimpleTypeVarTuple = (int, float),
**SimpleParamSpec = (str, bytearray),
](
a: SimpleTypeVar,
b: TypeVarWithDefault,
c: TypeVarWithBound,
d: Callable[SimpleParamSpec, TypeVarWithConstraints],
*e: SimpleTypeVarTuple,
): ...
8.10.1. Γενικές συναρτήσεις¶
Οι γενικές συναρτήσεις δηλώνονται ως εξής:
def func[T](arg: T): ...
Αυτή η σύνταξη είναι ισοδύναμη με το:
annotation-def TYPE_PARAMS_OF_func():
T = typing.TypeVar("T")
def func(arg: T): ...
func.__type_params__ = (T,)
return func
func = TYPE_PARAMS_OF_func()
Εδώ το annotation-def υποδηλώνει ένα annotation scope, το οποίο δεν δεσμεύεται στην πραγματικότητα σε κανένα όνομα κατά τον χρόνο εκτέλεσης. (Έχει ληφθεί άλλη μία ελευθερία στη μετάφραση: η σύνταξη δεν περνά μέσω της πρόσβασης σε χαρακτηριστικό πάνω στο typing module, αλλά δημιουργεί απευθείας ένα στιγμιότυπο της typing.TypeVar.)
Τα annotations των γενικών συναρτήσεων αξιολογούνται εντός του εύρους του annotation που χρησιμοποιείται για τη δήλωση των παραμέτρων τύπου, αλλά οι προεπιλογές και οι decorators της συνάρτησης όχι.
Το ακόλουθο παράδειγμα απεικονίζει τους κανόνες εμβέλειας για αυτές τις περιπτώσεις, καθώς και για επιπρόσθετα είδη παραμέτρων τύπου:
@decorator
def func[T: int, *Ts, **P](*args: *Ts, arg: Callable[P, T] = some_default):
...
Εκτός από το lazy evaluation του TypeVar bound, αυτό είναι ισοδύναμο με το:
DEFAULT_OF_arg = some_default
annotation-def TYPE_PARAMS_OF_func():
annotation-def BOUND_OF_T():
return int
# In reality, BOUND_OF_T() is evaluated only on demand.
T = typing.TypeVar("T", bound=BOUND_OF_T())
Ts = typing.TypeVarTuple("Ts")
P = typing.ParamSpec("P")
def func(*args: *Ts, arg: Callable[P, T] = DEFAULT_OF_arg):
...
func.__type_params__ = (T, Ts, P)
return func
func = decorator(TYPE_PARAMS_OF_func())
Τα κεφαλαιοποιημένα ονόματα όπως το DEFAULT_OF_arg δεν δεσμεύονται στην πραγματικότητα κατά τον χρόνο εκτέλεσης.
8.10.2. Γενικές κλάσεις¶
Οι γενικές κλάσεις δηλώνονται ως εξής:
class Bag[T]: ...
Αυτή η σύνταξη είναι ισοδύναμη με το:
annotation-def TYPE_PARAMS_OF_Bag():
T = typing.TypeVar("T")
class Bag(typing.Generic[T]):
__type_params__ = (T,)
...
return Bag
Bag = TYPE_PARAMS_OF_Bag()
Εδώ πάλι το annotation-def (δεν είναι πραγματική λέξη-κλειδί) υποδεικνύει ένα annotation scope, και το όνομα TYPE_PARAMS_OF_Bag δεν δεσμεύεται στην πραγματικότητα κατά τον χρόνο εκτέλεσης.
Οι γενικές κλάσεις κληρονομούν σιωπηρά την typing.Generic. Οι βασικές κλάσεις και τα ορίσματα λέξης-κλειδιού των γενικών κλάσεων αξιολογούνται εντός του εύρους του τύπου για τις παραμέτρους τύπου, και οι decorators αξιολογούνται έξω από αυτό το εύρος. Αυτό απεικονίζεται από αυτό το παράδειγμα:
@decorator
class Bag(Base[T], arg=T): ...
Αυτό είναι ισοδύναμο με το:
annotation-def TYPE_PARAMS_OF_Bag():
T = typing.TypeVar("T")
class Bag(Base[T], typing.Generic[T], arg=T):
__type_params__ = (T,)
...
return Bag
Bag = decorator(TYPE_PARAMS_OF_Bag())
8.10.3. Ψευδώνυμα γενικού τύπου¶
Η δήλωση type μπορεί επίσης να χρησιμοποιηθεί για τη δημιουργία ενός ψευδωνύμου γενικού τύπου:
type ListOrSet[T] = list[T] | set[T]
Με εξαίρεση το lazy evaluation της τιμής, αυτό είναι ισοδύναμο με το:
annotation-def TYPE_PARAMS_OF_ListOrSet():
T = typing.TypeVar("T")
annotation-def VALUE_OF_ListOrSet():
return list[T] | set[T]
# In reality, the value is lazily evaluated
return typing.TypeAliasType("ListOrSet", VALUE_OF_ListOrSet(), type_params=(T,))
ListOrSet = TYPE_PARAMS_OF_ListOrSet()
Εδώ, το annotation-def (δεν είναι πραγματική λέξη-κλειδί) υποδεικνύει ένα annotation scope. Τα κεφαλαιοποιημένα ονόματα όπως το TYPE_PARAMS_OF_ListOrSet δεν δεσμεύονται στην πραγματικότητα κατά τον χρόνο εκτέλεσης.
8.11. Annotations¶
Άλλαξε στην έκδοση 3.14: Τα annotations πλέον αξιολογούνται νωχελικά από προεπιλογή.
Οι μεταβλητές και οι παράμετροι συναρτήσεων μπορούν να φέρουν annotations, που δημιουργούνται προσθέτοντας άνω και κάτω τελεία μετά το όνομα, ακολουθούμενη από μια έκφραση:
x: annotation = 1
def f(param: annotation): ...
Οι συναρτήσεις μπορούν επίσης να φέρουν ένα annotation επιστροφής ακολουθώντας ένα βέλος:
def f() -> annotation: ...
Τα annotations χρησιμοποιούνται συμβατικά για type hints, αλλά αυτό δεν επιβάλλεται από τη γλώσσα, και γενικά τα annotations μπορούν να περιέχουν αυθαίρετες εκφράσεις. Η παρουσία των annotations δεν αλλάζει τη σημασιολογία εκτέλεσης του κώδικα, εκτός εάν χρησιμοποιείται κάποιος μηχανισμός που επιθεωρεί και χρησιμοποιεί τα annotations (όπως οι dataclasses ή η functools.singledispatch()).
Από προεπιλογή, τα annotations αξιολογούνται νωχελικά σε ένα annotation scope. Αυτό σημαίνει ότι δεν αξιολογούνται όταν αξιολογείται ο κώδικας που περιέχει το annotation. Αντίθετα, ο διερμηνέας αποθηκεύει πληροφορίες που μπορούν να χρησιμοποιηθούν για να αξιολογηθεί το annotation αργότερα, εάν ζητηθεί. Το module annotationlib παρέχει εργαλεία για την αξιολόγηση των annotations.
Εάν η μελλοντική δήλωση from __future__ import annotations είναι παρούσα, όλα τα annotations αποθηκεύονται αντί αυτού ως συμβολοσειρές:
>>> from __future__ import annotations
>>> def f(param: annotation): ...
>>> f.__annotations__
{'param': 'annotation'}
Αυτή η μελλοντική δήλωση θα θεωρηθεί απαρχαιωμένη και θα αφαιρεθεί σε μια μελλοντική έκδοση της Python, αλλά όχι προτού η Python 3.13 φτάσει στο τέλος της ζωής της (δείτε το PEP 749). Όταν χρησιμοποιείται, τα εργαλεία ενδοσκόπησης όπως η annotationlib.get_annotations() και η typing.get_type_hints() είναι λιγότερο πιθανό να μπορούν να επιλύσουν annotations κατά τον χρόνο εκτέλεσης.
Υποσημειώσεις