# 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_funcdef
    suite:         stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
    statement:     stmt_list NEWLINE | compound_stmt
    stmt_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_stmt
    try1_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 ) ":" suite
    with_stmt_contents: with_item ("," with_item)*
    with_item:          expression ["as" target]

Η εκτέλεση της εντολής `with` με ένα «αντικείμενο» προχωρά ως εξής:

1. Η έκφραση πλαισίου (η έκφραση που δίνεται στο `with_item`) αξιολογείται για να ληφθεί ένας διαχειριστής πλαισίου.

2. Φορτώνεται η `__enter__()` του διαχειριστή πλαισίου για μελλοντική χρήση.

3. Φορτώνεται η `__exit__()` του διαχειριστή πλαισίου για μελλοντική χρήση.

4. Καλείται η μέθοδος `__enter__()` του διαχειριστή πλαισίου.

5. Εάν συμπεριλήφθηκε ένας στόχος στην δήλωση `with`, η τιμή επιστροφής της `__enter__()` του ανατίθεται.

   Σημείωση

   Η δήλωση `with` εγγυάται ότι εάν η μέθοδος `__enter__()` επιστρέψει το αποτέλεσμα χωρίς σφάλμα, τότε η `__exit__()` θα κληθεί πάντα. Επομένως, εάν προκύψει σφάλμα κατά την ανάθεση στη λίστα-στόχο, θα αντιμετωπιστεί με τον ίδιο τρόπο όπως ένα σφάλμα που προκύπτει μέσα στη σουίτα. Δείτε το βήμα 7 παρακάτω.

6. Η σουίτα εκτελείται.

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: Υποστήριξη για χρήση παρενθέσεων ομαδοποίησης ώστε να διασπαστεί η δήλωση σε πολλές γραμμές.

Δείτε επίσης

PEP 343 - Η δήλωση «with»

  Οι προδιαγραφές, το υπόβαθρο και τα παραδείγματα για την δήλωση `with` της Python.

## 8.6. Η δήλωση `match`

Added in version 3.10.

Η δήλωση match χρησιμοποιείται για ταίριασμα προτύπων. Σύνταξη:

    match_stmt:   'match' subject_expr ":" NEWLINE INDENT case_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.

Δείτε επίσης

- PEP 634 – Ταίριασμα Δομικών Προτύπων: Προδιαγραφή

- PEP 636 – Ταίριασμα Δομικών Προτύπων: Εγχειρίδιο

### 8.6.1. Επισκόπηση

Ακολουθεί μια επισκόπηση της λογικής ροής μιας εντολής match:

1. Η έκφραση θέματος `subject_expr` αξιολογείται και λαμβάνεται η προκύπτουσα τιμή θέματος. Εάν η έκφραση θέματος περιέχει κόμμα, κατασκευάζεται μία πλειάδα χρησιμοποιώντας τους τυπικούς κανόνες.

2. Κάθε πρότυπο σε ένα `case_block` προσπαθεί να ταιριάξει με την τιμή του θέματος. Οι συγκεκριμένοι κανόνες για επιτυχία ή αποτυχία περιγράφονται παρακάτω. Η προσπάθεια ταιριάσματος μπορεί επίσης να δεσμεύσει κάποια ή όλα τα αυτόνομα ονόματα εντός του προτύπου. Οι ακριβείς κανόνες δέσμευσης προτύπων διαφέρουν ανά τύπο προτύπου και καθορίζονται παρακάτω. Οι δεσμεύσεις ονομάτων, που γίνονται κατά τη διάρκεια ενός επιτυχημένου ταιριάσματος προτύπου, επιβιώνουν από το εκτελεσμένο μπλοκ και μπορούν να χρησιμοποιηθούν μετά την δήλωση match.

   Σημείωση

   Κατά τη διάρκεια αποτυχημένων ταιριασμάτων προτύπων, ορισμένα υπο-πρότυπα μπορεί να πετύχουν. Μην βασίζεστε στο ότι θα πραγματοποιηθούν δεσμεύσεις για ένα αποτυχημένο ταίριασμα. Αντίστροφα, μην βασίζεστε στο ότι οι μεταβλητές θα παραμείνουν αμετάβλητες μετά από ένα αποτυχημένο ταίριασμα. Η ακριβής συμπεριφορά εξαρτάται από την υλοποίηση και μπορεί να διαφέρει. Πρόκειται για μια σκόπιμη απόφαση ώστε να επιτραπεί σε διαφορετικές υλοποιήσεις να προσθέσουν βελτιστοποιήσεις.

3. Εάν το πρότυπο επιτύχει, αξιολογείται ο αντίστοιχος 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` έχει ως εξής:

1. Ελέγχει ότι το πρότυπο στο μπλοκ `case` επέτυχε. Εάν το πρότυπο απέτυχε, ο `guard` δεν αξιολογείται και ελέγχεται το επόμενο μπλοκ `case`.

2. Εάν το πρότυπο επέτυχε, αξιολογείται ο `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 | pattern
    pattern:        as_pattern | or_pattern
    closed_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: attr
    attr:          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 | pattern
    star_pattern:           "*" (capture_pattern | wildcard_pattern)

Δεν υπάρχει διαφορά, εάν χρησιμοποιούνται παρενθέσεις ή αγκύλες για τα μοτίβα ακολουθίας (δηλαδή `(...)` έναντι `[...]`).

Σημείωση

Ένα μεμονωμένο μοτίβο περικλειόμενο σε παρενθέσεις χωρίς τελικό κόμμα (π.χ. `(3 | 4)`) είναι ένα μοτίβο ομαδοποίησης. Ενώ ένα μεμονωμένο μοτίβο περικλειόμενο σε αγκύλες (π.χ. `[3 | 4]`) εξακολουθεί να είναι ένα μοτίβο ακολουθίας.

Μπορεί να υπάρχει το πολύ ένα υπο-μοτίβο με αστερίσκο σε ένα μοτίβο ακολουθίας. Το υπο-μοτίβο με αστερίσκο μπορεί να εμφανιστεί σε οποιαδήποτε θέση. Εάν δεν υπάρχει υπο-μοτίβο με αστερίσκο, το μοτίβο ακολουθίας είναι σταθερού μήκους· διαφορετικά είναι μεταβλητού μήκους.

Ακολουθεί η λογική ροή για το ταίριασμα ενός μοτίβου ακολουθίας έναντι μιας τιμής θέματος:

1. Εάν η τιμή του θέματος δεν είναι μια ακολουθία [2], το μοτίβο ακολουθίας αποτυγχάνει.

2. Εάν η τιμή του θέματος είναι στιγμιότυπο των `str`, `bytes` ή `bytearray` το μοτίβο ακολουθίας αποτυγχάνει.

3. Τα επόμενα βήματα εξαρτώνται από το αν το μοτίβο ακολουθίας είναι σταθερού ή μεταβλητού μήκους.

   Εάν το μοτίβο ακολουθίας είναι σταθερού μήκους:

   1. Εάν το μήκος της ακολουθίας θέματος δεν είναι ίσο με τον αριθμό των υπο-μοτίβων, το μοτίβο ακολουθίας αποτυγχάνει

   2. Τα υπο-μοτίβα στο μοτίβο ακολουθίας ταιριάζουν με τα αντίστοιχα στοιχεία τους στην ακολουθία θέματος από αριστερά προς τα δεξιά. Το ταίριασμα σταματά μόλις αποτύχει ένα υπο-μοτίβο. Εάν όλα τα υπο-μοτίβα επιτύχουν στο ταίριασμα του αντίστοιχου στοιχείου τους, το μοτίβο ακολουθίας επιτυγχάνει.

   Διαφορετικά, εάν το μοτίβο ακολουθίας είναι μεταβλητού μήκους:

   1. Εάν το μήκος της ακολουθίας θέματος είναι μικρότερο από τον αριθμό των υπο-μοτίβων χωρίς αστερίσκο, το μοτίβο ακολουθίας αποτυγχάνει.

   2. Τα αρχικά υπο-μοτίβα χωρίς αστερίσκο ταιριάζουν με τα αντίστοιχα στοιχεία τους όπως και για τις ακολουθίες σταθερού μήκους.

   3. Εάν το προηγούμενο βήμα επιτύχει, το υπο-μοτίβο με αστερίσκο ταιριάζει με μια λίστα που σχηματίζεται από τα εναπομείναντα στοιχεία του θέματος, εξαιρουμένων των στοιχείων που αντιστοιχούν σε υπο-μοτίβα χωρίς αστερίσκο τα οποία ακολουθούν το υπο-μοτίβο με αστερίσκο.

   4. Τα εναπομείναντα υπο-μοτίβα χωρίς αστερίσκο ταιριάζουν με τα αντίστοιχα στοιχεία του θέματος, όπως και για μια ακολουθία σταθερού μήκους.

   Σημείωση

   Το μήκος της ακολουθίας θέματος λαμβάνεται μέσω της `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_pattern
    double_star_pattern: "**" capture_pattern

Μπορεί να υπάρχει το πολύ ένα μοτίβο με διπλό αστερίσκο σε ένα μοτίβο αντιστοίχισης. Το μοτίβο με διπλό αστερίσκο πρέπει να είναι το τελευταίο υπο-μοτίβο στο μοτίβο αντιστοίχισης.

Δεν επιτρέπονται διπλότυπα κλειδιά σε μοτίβα αντιστοίχισης. Τα διπλότυπα λεκτικά κλειδιά θα κάνουν raise ένα `SyntaxError`. Δύο κλειδιά που κατά τα άλλα έχουν την ίδια τιμή θα κάνουν raise ένα `ValueError` κατά τον χρόνο εκτέλεσης.

Ακολουθεί η λογική ροή για το ταίριασμα ενός μοτίβου αντιστοίχισης έναντι μιας τιμής θέματος:

1. Εάν η τιμή του θέματος δεν είναι μία αντιστοίχιση [3], το μοτίβο αντιστοίχισης αποτυγχάνει.

2. Εάν κάθε κλειδί που δίνεται στο μοτίβο αντιστοίχισης είναι παρόν στην αντιστοίχιση του θέματος, και το μοτίβο για κάθε κλειδί ταιριάζει με το αντίστοιχο στοιχείο της αντιστοίχισης του θέματος, το μοτίβο αντιστοίχισης επιτυγχάνει.

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_patterns
    positional_patterns: ",".pattern+
    keyword_patterns:    ",".keyword_pattern+
    keyword_pattern:     NAME "=" pattern

Η ίδια λέξη-κλειδί δεν πρέπει να επαναλαμβάνεται στα μοτίβα κλάσης.

Ακολουθεί η λογική ροή για το ταίριασμα ενός μοτίβου κλάσης έναντι μιας τιμής θέματος:

1. Εάν το `name_or_attr` δεν είναι στιγμιότυπο του ενσωματωμένου `type`, γίνεται raise ένα `TypeError`.

2. Εάν η τιμή του θέματος δεν είναι στιγμιότυπο του `name_or_attr` (ελέγχεται μέσω της `isinstance()`), το μοτίβο κλάσης αποτυγχάνει.

3. Εάν δεν υπάρχουν ορίσματα μοτίβου, το μοτίβο επιτυγχάνει. Διαφορετικά, τα επόμενα βήματα εξαρτώνται από το αν υπάρχουν μοτίβα λέξης-κλειδιού ή ορισμάτων θέσης.

   Για έναν αριθμό ενσωματωμένων τύπων (καθορίζονται παρακάτω), γίνεται αποδεκτό ένα μεμονωμένο υπο-μοτίβο θέσης το οποίο θα ταιριάξει με ολόκληρο το θέμα· για αυτούς τους τύπους, τα μοτίβα λέξης-κλειδιού λειτουργούν επίσης όπως και για άλλους τύπους.

   Εάν υπάρχουν μόνο μοτίβα λέξης-κλειδιού, επεξεργάζονται ως εξής, ένα προς ένα:

   1. Η λέξη-κλειδί αναζητείται ως χαρακτηριστικό πάνω στο θέμα.

      - Εάν κάνει raise μια εξαίρεση διαφορετική από το `AttributeError`, η εξαίρεση ανεβαίνει προς τα πάνω.

      - Εάν κάνει raise το `AttributeError`, το μοτίβο κλάσης έχει αποτύχει.

      - Αλλιώς, το υπο-μοτίβο που σχετίζεται με το μοτίβο λέξης-κλειδιού ταιριάζεται με την τιμή του χαρακτηριστικού του θέματος. Εάν αυτό αποτύχει, το μοτίβο κλάσης αποτυγχάνει· εάν επιτύχει, το ταίριασμα προχωρά στην επόμενη λέξη-κλειδί.

   2. Εάν όλα τα μοτίβα λέξης-κλειδιού επιτύχουν, το μοτίβο κλάσης επιτυγχάνει.

   Εάν υπάρχουν οποιαδήποτε μοτίβα θέσης, μετατρέπονται σε μοτίβα λέξης-κλειδιού χρησιμοποιώντας το χαρακτηριστικό `__match_args__` πάνω στην κλάση `name_or_attr` πριν από το ταίριασμα:

   1. Καλείται το ισοδύναμο της `getattr(cls, "__match_args__", ())`.

      - Εάν κάνει raise μια εξαίρεση, η εξαίρεση ανεβαίνει προς τα πάνω.

      - Εάν η επιστρεφόμενη τιμή δεν είναι πλειάδα, η μετατροπή αποτυγχάνει και γίνεται raise το `TypeError`.

      - Εάν υπάρχουν περισσότερα μοτίβα θέσης από το `len(cls.__match_args__)`, γίνεται raise το `TypeError`.

      - Διαφορετικά, το μοτίβο θέσης `i` μετατρέπεται σε ένα μοτίβο λέξης-κλειδιού χρησιμοποιώντας το `__match_args__[i]` ως λέξη-κλειδί. Το `__match_args__[i]` πρέπει να είναι συμβολοσειρά· εάν όχι, γίνεται raise το `TypeError`.

      - Εάν υπάρχουν διπλότυπες λέξεις-κλειδιά, γίνεται raise το `TypeError`.

      Δείτε επίσης

      Προσαρμογή ορισμάτων θέσης στην αντιστοίχιση προτύπων κλάσης

   2. Μόλις όλα τα μοτίβα θέσης μετατραπούν σε μοτίβα λέξης-κλειδιού, το ταίριασμα προχωρά σαν να υπήρχαν μόνο μοτίβα λέξης-κλειδιού.

   Για τους ακόλουθους ενσωματωμένους τύπους, ο χειρισμός των υπο-μοτίβων θέσης είναι διαφορετικός:

   - `bool`

   - `bytearray`

   - `bytes`

   - `dict`

   - `float`

   - `frozenset`

   - `int`

   - `list`

   - `set`

   - `str`

   - `tuple`

   Αυτές οι κλάσεις δέχονται ένα μόνο όρισμα θέσης, και το μοτίβο εκεί ταιριάζεται έναντι ολόκληρου του αντικειμένου αντί για ένα χαρακτηριστικό. Για παράδειγμα το `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`

- … και ούτω καθεξής για το αντίστοιχο ζεύγος ορίσματος λέξης-κλειδιού/μοτίβου.

Δείτε επίσης

- PEP 634 – Ταίριασμα Δομικών Προτύπων: Προδιαγραφή

- PEP 636 – Ταίριασμα Δομικών Προτύπων: Εγχειρίδιο

## 8.7. Ορισμοί συναρτήσεων

Ένας ορισμός συνάρτησης ορίζει ένα αντικείμενο συνάρτησης ορισμένο από τον χρήστη (δείτε την ενότητα Η τυπική ιεραρχία τύπου):

    funcdef:                   [decorators] "def" funcname [type_params] "(" [parameter_list] ")"
                               ["->" expression] ":" suite
    decorators:                decorator+
    decorator:                 "@" assignment_expression NEWLINE
    parameter_list:            defparameter ("," defparameter)* "," "/" ["," [parameter_list_no_posonly]]
                                 | parameter_list_no_posonly
    parameter_list_no_posonly: defparameter ("," defparameter)* ["," [parameter_list_starargs]]
                               | parameter_list_starargs
    parameter_list_starargs:   "*" [star_parameter] ("," defparameter)* ["," [parameter_star_kwargs]]
                               | "*" ("," defparameter)+ ["," [parameter_star_kwargs]]
                               | parameter_star_kwargs
    parameter_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] ":" suite
    inheritance: "(" [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 | paramspec
    typevar:      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 κατά τον χρόνο εκτέλεσης.

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

[1]

Η εξαίρεση διαδίδεται στη στοίβα κλήσεων εκτός εάν υπάρχει μία ρήτρα `finally` η οποία τυχαίνει να κάνει raise μια άλλη εξαίρεση. Αυτή η νέα εξαίρεση προκαλεί την απώλεια της παλιάς.

[2]

Στο ταίριασμα μοτίβων, μια ακολουθία ορίζεται ως ένα από τα ακόλουθα:

- μια κλάση που κληρονομεί από την `collections.abc.Sequence`

- μια Python κλάση που έχει εγγραφεί ως `collections.abc.Sequence`

- μια ενσωματωμένη κλάση που έχει το `Py_TPFLAGS_SEQUENCE` bit της ρυθμισμένο (στην CPython)

- μια κλάση που κληρονομεί οποιοδήποτε από τα παραπάνω

Οι ακόλουθες κλάσεις της τυπικής βιβλιοθήκης είναι ακολουθίες:

- `array.array`

- `collections.deque`

- `list`

- `memoryview`

- `range`

- `tuple`

Σημείωση

Τιμές θέματος τύπου `str`, `bytes`, και `bytearray` δεν ταιριάζουν με μοτίβα ακολουθίας.

[3]

Στο ταίριασμα μοτίβων, μία αντιστοίχιση ορίζεται ως ένα από τα ακόλουθα:

- μια κλάση που κληρονομεί την `collections.abc.Mapping`

- μια Python κλάση που έχει εγγραφεί ως `collections.abc.Mapping`

- μια ενσωματωμένη κλάση που έχει το `Py_TPFLAGS_MAPPING` bit της ρυθμισμένο (στην CPython)

- μια κλάση που κληρονομεί οποιοδήποτε από τα παραπάνω

Οι κλάσεις `dict` και `types.MappingProxyType` της τυπικής βιβλιοθήκης είναι αντιστοιχίσεις.

[4]

Ένα λεκτικό συμβολοσειράς που εμφανίζεται ως η πρώτη δήλωση στο σώμα μιας συνάρτησης μετατρέπεται στο χαρακτηριστικό `__doc__` της συνάρτησης και κατά συνέπεια στο docstring της συνάρτησης.

[5]

Ένα λεκτικό συμβολοσειράς που εμφανίζεται ως η πρώτη δήλωση στο σώμα μιας κλάσης μετατρέπεται στο στοιχείο `__doc__` του χώρου ονομάτων και κατά συνέπεια στο docstring της κλάσης.