# 4. Περισσότερα εργαλεία Ελέγχου Ροής

Εκτός από τη πρόταση `while` που μόλις εισήχθη, η Python χρησιμοποιεί μερικές ακόμη που θα συναντήσουμε σε αυτό το κεφάλαιο.

## 4.1. Προτάσεις `if`

Ίσως ο πιο γνωστός τύπος statement είναι η πρόταση `if`. Για παράδειγμα:

    >>> x = int(input("Please enter an integer: "))
    Please enter an integer: 42
    >>> if x < 0:
    ...     x = 0
    ...     print('Negative changed to zero')
    ... elif x == 0:
    ...     print('Zero')
    ... elif x == 1:
    ...     print('Single')
    ... else:
    ...     print('More')
    ...
    More

Μπορεί να υπάρχουν μηδέν ή περισσότερα μέρη `elif` και το τμήμα `else` είναι προαιρετικό. To keyword “`elif`” είναι συντομογραφία του “else if”, και είναι χρήσιμη για να αποφύγετε την υπερβολική εσοχή. Μια ακολουθία `if` … `elif` … `elif` … είναι υποκατάστατο των δηλώσεων `switch` ή `case` που υπάρχουν σε άλλες γλώσσες.

Εάν συγκρίνετε την ίδια τιμή με πολλές σταθερές ή ελέγχετε για συγκεκριμένους τύπους ή χαρακτηριστικά, μπορεί επίσης να βρείτε χρήσιμη τη δήλωση `match` Για περισσότερες λεπτομέρειες, ανατρέξτε στο Προτάσεις match.

## 4.2. Προτάσεις `for`

Η δήλωση `for` στην Python διαφέρει λίγο από αυτό που μπορεί να έχετε συνηθίσει στη C ή στην Pascal. Αντί να επαναλαμβάνετε πάντα μια αριθμητική πρόοδο αριθμών (όπως στη Pascal) ή να δίνετε στον χρήστη τη δυνατότητα να ορίσει τόσο το βήμα επανάληψης όσο και τη συνθήκη διακοπής (όπως C),η δήλωση της Python `for` επαναλαμβάνεται πάνω από τα στοιχεία οποιασδήποτε ακολουθίας (λίστας ή συμβολοσειράς), με τη σειρά που εμφανίζονται στην ακολουθία. Για παράδειγμα (χωρίς λογοπαίγνιο):

    >>> # Measure some strings:
    >>> words = ['cat', 'window', 'defenestrate']
    >>> for w in words:
    ...     print(w, len(w))
    ...
    cat 3
    window 6
    defenestrate 12

Ο κώδικας που τροποποιεί μια συλλογή ενώ επαναλαμβάνεται πάνω από την ίδια συλλογή μπορεί να είναι δύσκολος για να γίνει σωστός. Αντίθετα, είναι συνήθως πιο απλό να κάνετε loop πάνω από ένα αντίγραφο συλλογής ή να δημιουργήσετε μια νέα συλλογή:

    # Create a sample collection
    users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'}

    # Strategy:  Iterate over a copy
    for user, status in users.copy().items():
        if status == 'inactive':
            del users[user]

    # Strategy:  Create a new collection
    active_users = {}
    for user, status in users.items():
        if status == 'active':
            active_users[user] = status

## 4.3. Η συνάρτηση `range()`

Εάν χρειάζεται να κάνετε επανάληψη σε μια ακολουθία αριθμών, η ενσωματωμένη (built-in) συνάρτηση `range()` είναι χρήσιμη. Δημιουργεί αριθμητικές προόδους:

    >>> for i in range(5):
    ...     print(i)
    ...
    0
    1
    2
    3
    4

Το δεδομένο τελικό σημείο δεν αποτελεί ποτέ μέρος της παραγόμενης ακολουθίας· το `range(10)` δημιουργεί 10 τιμές, τους δείκτες για στοιχεία μιας ακολουθίας μήκους 10. Είναι δυνατόν να αφήσουμε το εύρος να ξεκινά από άλλο αριθμό ή για να καθορίσετε μια διαφορετική προσαύξηση (ακόμη και αρνητική, μερικές φορές αυτό ονομάζεται “βήμα”):

    >>> list(range(5, 10))
    [5, 6, 7, 8, 9]

    >>> list(range(0, 10, 3))
    [0, 3, 6, 9]

    >>> list(range(-10, -100, -30))
    [-10, -40, -70]

Για να γίνουν iterate οι δείκτες μια ακολουθίας, μπορείτε να συνδυάσετε τις `range()` και `len()` ως εξής:

    >>> a = ['Mary', 'had', 'a', 'little', 'lamb']
    >>> for i in range(len(a)):
    ...     print(i, a[i])
    ...
    0 Mary
    1 had
    2 a
    3 little
    4 lamb

Στις περισσότερες τέτοιες περιπτώσεις, ωστόσο, είναι βολικό να χρησιμοποιήσετε τη συνάρτηση `enumerate()`, δείτε Τεχνικές Looping.

Ένα περίεργο πράγμα συμβαίνει αν απλώς εκτυπώσετε ένα range:

    >>> range(10)
    range(0, 10)

Με πολλούς τρόπους το αντικείμενο που επιστρέφεται από το `range()` συμπεριφέρεται σαν να είναι μια λίστα, αλλά στην πραγματικότητα δεν είναι. Είναι ένα αντικείμενο που επιστρέφει τα διαδοχικά στοιχεία της επιθυμητής ακολουθίας όταν κάνετε επανάληψη πάνω του, αλλά δεν μπαίνει πραγματικά στη λίστα, εξοικονομώντας έτσι χώρο.

Λέμε ότι ένα τέτοιο αντικείμενο είναι iterable, δηλαδή, κατάλληλο ως στόχος για συναρτήσεις και κατασκευές που περιμένουν κάτι από το οποίο μπορούν να λάβουν διαδοχικά στοιχεία μέχρι να εξαντληθεί η προσφορά. Είδαμε ότι η δήλωση `for` είναι μια τέτοια κατασκευή, ενώ ένα παράδειγμα συνάρτησης που παίρνει ένα iterable είναι η `sum()`:

    >>> sum(range(4))  # 0 + 1 + 2 + 3
    6

Later we will see more functions that return iterables and take iterables as arguments. In chapter Δομές Δεδομένων, we will discuss `list()` in more detail.

## 4.4. `break` και `continue` Προτάσεις

Η δήλωση `break` ξεφεύγει από τον πιο εσωτερικό βρόχο `for` ή `while`:

    >>> for n in range(2, 10):
    ...     for x in range(2, n):
    ...         if n % x == 0:
    ...             print(f"{n} equals {x} * {n//x}")
    ...             break
    ...
    4 equals 2 * 2
    6 equals 2 * 3
    8 equals 2 * 4
    9 equals 3 * 3

Η δήλωση `continue` συνεχίζεται με την επόμενη επανάληψη του βρόχου:

    >>> for num in range(2, 10):
    ...     if num % 2 == 0:
    ...         print(f"Found an even number {num}")
    ...         continue
    ...     print(f"Found an odd number {num}")
    ...
    Found an even number 2
    Found an odd number 3
    Found an even number 4
    Found an odd number 5
    Found an even number 6
    Found an odd number 7
    Found an even number 8
    Found an odd number 9

## 4.5. `else` Ρήτρες στους βρόχους

Σε έναν βρόχο `for` ή `while` η πρόταση `break` μπορεί να συνδυαστεί με μια πρόταση `else`. Εάν ο βρόχος τελειώσει χωρίς να εκτελεστεί η `break`, εκτελείται ο όρος `else`.

Σε έναν βρόχο `for`, η πρόταση `else` εκτελείται αφού ο βρόχος ολοκληρώσει στην τελική του επανάληψη, δηλαδή αν δεν σημειωθεί διάλειμμα.

Σε έναν βρόχο `while`, εκτελείται αφού η συνθήκη του βρόχου είναι ψευδής.

Σε οποιοδήποτε είδος βρόχου, η πρόταση `else` δεν εκτελείται εάν ο βρόχος τερματίστηκε με μια λέξη `break`. Φυσικά, άλλοι τρόποι για να τερματίσετε νωρίς τον βρόχο, όπως ένα `return` ή μια αυξημένη εξαίρεση, θα παρακάμψουν επίσης την εκτέλεση της πρότασης `else`.

Αυτό αποδεικνύεται στον ακόλουθο βρόχο `for`, που αναζητά πρώτους αριθμούς:

    >>> for n in range(2, 10):
    ...     for x in range(2, n):
    ...         if n % x == 0:
    ...             print(n, 'equals', x, '*', n//x)
    ...             break
    ...     else:
    ...         # loop fell through without finding a factor
    ...         print(n, 'is a prime number')
    ...
    2 is a prime number
    3 is a prime number
    4 equals 2 * 2
    5 is a prime number
    6 equals 2 * 3
    7 is a prime number
    8 equals 2 * 4
    9 equals 3 * 3

(Ναι, αυτός είναι ο σωστός κώδικας. Κοιτάξτε προσεκτικά: η πρόταση `else` ανήκει στον βρόχο `for`, όχι στη δήλωση if.)

Ένας τρόπος για να σκεφτούμε την ρήτρα else είναι να τη φανταστούμε σε σύζευξη με το `if` μέσα στον βρόχο. Καθώς εκτελείται ο βρόχος, θα εκτελέσει μια ακολουθία όπως if/if/if/else. Το `if` βρίσκεται μέσα στον βρόχο, συναντάται πολλές φορές. Εάν η συνθήκη είναι ποτέ αληθής, θα συμβεί `break`. Εάν η συνθήκη δεν είναι ποτέ αληθής, θα εκτελεστεί ο όρος `else` εκτός του βρόχου.

Όταν χρησιμοποιείται με έναν βρόχο, η πρόταση `else` έχει περισσότερα κοινά με την πρόταση `else` μιας πρότασης `try` παρά με αυτήν των statements `if` η πρόταση `else` μιας `try` εκτελείται όταν δεν υπάρχει εξαίρεση, και η πρόταση `else` ενός βρόχου εκτελείται όταν δεν υπάρχει `break`. Για περισσότερα με τη δήλωση `try` και τις εξαιρέσεις, δείτε Διαχείριση Εξαιρέσεων.

## 4.6. Προτάσεις `pass`

Η δήλωση `pass` δεν κάνει τίποτα. Μπορεί να χρησιμοποιηθεί όταν απαιτείται συντακτικά μια πρόταση, αλλά το πρόγραμμα δεν απαιτεί καμία ενέργεια. Για παράδειγμα:

    >>> while True:
    ...     pass  # Busy-wait for keyboard interrupt (Ctrl+C)
    ...

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

    >>> class MyEmptyClass:
    ...     pass
    ...

Ένα άλλο μέρος `pass` που μπορεί να χρησιμοποιηθεί είναι ως place-holder για μια συνάρτηση ή το σώμα υπό όρους όταν εργάζεστε σε νέο κώδικα, επιτρέποντας σας να συνεχίσετε να σκέφτεστε σε ένα πιο αφηρημένο επίπεδο. Το `pass` αγνοείται σιωπηλά:

    >>> def initlog(*args):
    ...     pass   # Remember to implement this!
    ...

Για αυτήν την τελευταία περίπτωση, πολλοί άνθρωποι χρησιμοποιούν το literale αποσιωπητικών `...` αντί για `pass`. Αυτή η χρήση δεν έχει ειδική σημασία για την Python και δεν αποτελεί μέρος του ορισμού της γλώσσας (θα μπορούσατε να χρησιμοποιήσετε οποιαδήποτε σταθερή έκφραση εδώ), αλλά το `...` χρησιμοποιείται συμβατικά και ως σώμα placeholder. Δείτε Το αντικείμενο Ellipsis.

## 4.7. Προτάσεις `match`

Μια δήλωση `match` παίρνει μια έκφραση και συγκρίνει την τιμή της με διαδοχικά μοτίβα που δίνονται ως ένα ή περισσότερα μπλοκ πεζών-κεφαλαίων. Αυτή είναι επιφανειακά παρόμοια με μια πρόταση switch στην C, Java ή JavaScript (και πολλές άλλες γλώσσες), αλλά είναι πιο παρόμοια με την αντιστοίχιση προτύπων σε γλώσσες όπως η Rust ή η Haskell. Εκτελείται μόνο το πρώτο μοτίβο που ταιριάζει και μπορεί επίσης να εξαγάγει στοιχεία (στοιχεία ακολουθίας ή ιδιότητες αντικειμένου) από την τιμή σε μεταβλητές. Εάν καμία περίπτωση δεν ταιριάζει, κανένας από τους κλάδους δεν εκτελείται.

Η απλούστερη φόρμα συγκρίνει μια τιμή θέματος με ένα ή περισσότερα literals:

    def http_error(status):
        match status:
            case 400:
                return "Bad request"
            case 404:
                return "Not found"
            case 418:
                return "I'm a teapot"
            case _:
                return "Something's wrong with the internet"

Σημειώστε το τελευταίο μπλοκ: το «variable name» `_` λειτουργεί ως μπαλαντέρ και δεν αποτυγχάνει ποτέ να ταιριάζει.

Μπορείτε να συνδυάσετε πολλά γράμματα σε ένα μόνο μοτίβο χρησιμοποιώντας το `|` («ή»):

    case 401 | 403 | 404:
        return "Not allowed"

Τα μοτίβα μπορεί να μοιάζουν με αναθέσεις unpacking, και μπορούν να χρησιμοποιηθούν για τη σύνδεση μεταβλητών:

    # point is an (x, y) tuple
    match point:
        case (0, 0):
            print("Origin")
        case (0, y):
            print(f"Y={y}")
        case (x, 0):
            print(f"X={x}")
        case (x, y):
            print(f"X={x}, Y={y}")
        case _:
            raise ValueError("Not a point")

Μελετήστε το ένα προσεκτικά! Το πρώτο μοτίβο έχει δύο literals, και μπορεί να θεωρηθεί ως επέκταση του literal μοτίβου που φαίνεται παραπάνω. Αλλά τα επόμενα δύο μοτίβα συνδυάζουν ένα literal σε μια μεταβλητή, και η μεταβλητή δεσμεύει μια τιμή από το θέμα (`point`). Το τέταρτο μοτίβο συλλαμβάνει δύο τιμές, γεγονός που το κάνει εννοιολογικά παρόμοιο με την ανάθεση unpacking `(x, y) = point`.

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

    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y

    def where_is(point):
        match point:
            case Point(x=0, y=0):
                print("Origin")
            case Point(x=0, y=y):
                print(f"Y={y}")
            case Point(x=x, y=0):
                print(f"X={x}")
            case Point():
                print("Somewhere else")
            case _:
                print("Not a point")

Μπορείτε να χρησιμοποιήσετε παραμέτρους θέσης με ορισμένες ενσωματωμένες κλάσεις που παρέχουν μια σειρά για τα χαρακτηριστικά τους (π.χ. κλάσεις δεδομένων). Μπορείτε επίσης να ορίσετε μια συγκεκριμένη θέση για χαρακτηριστικά σε μοτίβα, ορίζοντας το ειδικό χαρακτηριστικό `__match_args__` στις κλάσεις σας. Εάν έχει οριστεί σε («x», «y»), τα ακόλουθα μοτίβα είναι όλα ισοδύναμα (και όλα δεσμεύουν το χαρακτηριστικό `y` στη μεταβλητή `var`):

    Point(1, var)
    Point(1, y=var)
    Point(x=1, y=var)
    Point(y=var, x=1)

Ένας συνιστώμενος τρόπος για να διαβάσετε τα μοτίβα είναι να τα δείτε ως μια εκτεταμένη μορφή αυτού που θα βάλετε στα αριστερά μιας ανάθεσης, για να κατανοήσετε ποιες μεταβλητές θα οριστούν σε τι. Μόνο τα ανεξάρτητα ονόματα (όπως `var` παραπάνω) εκχωρούνται από μια δήλωση αντιστοίχισης. Ονόματα με κουκκίδες (όπως `foo.bar`), ονόματα χαρακτηριστικών (τα `x=` και `y=` παραπάνω) ή ονόματα κλάσεων (αναγνωρίζονται από το «(…)» που βρίσκεται δίπλα όπως το `Point` παραπάνω) δεν ανατίθενται ποτέ.

Τα μοτίβα μπορούν να είναι αυθαίρετα ένθετα. Για παράδειγμα, εάν έχουμε μια σύντομη λίστα με πόντους, με προσθήκη `__match_args__`, θα μπορούσαμε να την αντιστοιχίσουμε ως εξής:

    class Point:
        __match_args__ = ('x', 'y')
        def __init__(self, x, y):
            self.x = x
            self.y = y

    match points:
        case []:
            print("No points")
        case [Point(0, 0)]:
            print("The origin")
        case [Point(x, y)]:
            print(f"Single point {x}, {y}")
        case [Point(0, y1), Point(0, y2)]:
            print(f"Two on the Y axis at {y1}, {y2}")
        case _:
            print("Something else")

Μπορούμε να προσθέσουμε μια πρόταση `if` σε ένα μοτίβο, γνωστό ως «guard». Εάν το guard είναι false, το `match` συνεχίζει για να δοκιμάσει το επόμενο μπλοκ πεζών-κεφαλαίων. Λάβετε υπόψη ότι η σύλληψη της τιμής γίνεται πριν ο guard αξιολογηθεί:

    match point:
        case Point(x, y) if x == y:
            print(f"Y=X at {x}")
        case Point(x, y):
            print(f"Not on the diagonal")

Πολλά άλλα βασικά χαρακτηριστικά αυτής της δήλωσης:

- Όπως το unpacking αναθέσεων, τα μοτίβα πλειάδας (tuple) και λίστας έχουν ακριβώς την ίδια σημασία και ταιριάζουν πραγματικά με αυθαίρετες ακολουθίες. Μια σημαντική εξαίρεση είναι ότι δεν ταιριάζουν με iterators ή συμβολοσειρές.

- Τα μοτίβα ακολουθίας υποστηρίζουν εκτεταμένο unpacking: `[x, y, *rest]` και `(x, y, *rest)` λειτουργεί παρόμοια με το unpacking αναθέσεων. Το όνομα μετά το `*` μπορεί επίσης να είναι `_`, οπότε το `(x, y, *_)` αντιστοιχεί σε μια ακολουθία τουλάχιστον δύο στοιχείων χωρίς να δεσμεύει τα υπόλοιπα στοιχεία.

- Μοτίβα αντιστοίχισης: `{"bandwidth": b, "latency": l}` καταγράφει τις τιμές `"bandwidth"` και `"latency"` από ένα λεξικό. Σε αντίθεση με τα μοτίβα ακολουθίας, επιπλέον κλειδιά αγνοούνται. Υποστηρίζεται επίσης το unpacking όπως το `**rest`. (Αλλά το `**_` θα ήταν περιττό, επομένως δεν επιτρέπεται.)

- Τα δευτερεύοντα μοτίβα μπορούν να αποτυπωθούν χρησιμοποιώντας το keyword `as`:

      case (Point(x1, y1), Point(x2, y2) as p2): ...

  θα καταγράψει το δεύτερο στοιχείο της εισόδου ως `p2` (εφόσον η είσοδος είναι μια ακολουθία δύο σημείων)

- Τα περισσότερα literals συγκρίνονται με ισότητα, ωστόσο τα singletons `True`, `False` και `None` συγκρίνονται με ταυτότητα.

- Patterns may use named constants. These must be dotted names to prevent them from being interpreted as capture variables:

      from enum import Enum
      class Color(Enum):
          RED = 'red'
          GREEN = 'green'
          BLUE = 'blue'

      color = Color(input("Enter your choice of 'red', 'blue' or 'green': "))

      match color:
          case Color.RED:
              print("I see red!")
          case Color.GREEN:
              print("Grass is green")
          case Color.BLUE:
              print("I'm feeling the blues :(")

Για πιο λεπτομερή επεξήγηση και πρόσθετα παραδείγματα, μπορείτε να δείτε το PEP 636 το οποίο είναι γραμμένο σε μορφή εκμάθησης.

## 4.8. Καθορισμός Συναρτήσεων

Μπορούμε να δημιουργήσουμε μια συνάρτηση που γράφει τη σειρά Fibonacci σε ένα αυθαίρετο όριο:

    >>> def fib(n):    # write Fibonacci series less than n
    ...     """Print a Fibonacci series less than n."""
    ...     a, b = 0, 1
    ...     while a < n:
    ...         print(a, end=' ')
    ...         a, b = b, a+b
    ...     print()
    ...
    >>> # Now call the function we just defined:
    >>> fib(2000)
    0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

Το keyword `def` εισάγει μια συνάρτηση ορισμός. Πρέπει να ακολουθείται από το όνομα της συνάρτησης και τη λίστα των τυπικών παραμέτρων σε παρένθεση. Οι δηλώσεις που σχηματίζουν το σώμα της συνάρτησης ξεκινούν από την επόμενη γραμμή και πρέπει να είναι εσοχές.

Η πρώτη δήλωση του σώματος της συνάρτησης μπορεί προαιρετικά να είναι ένα literal συμβολοσειράς· αυτό το literal συμβολοσειράς είναι η συμβολοσειρά τεκμηρίωσης της συνάρτησης ή docstring. (Περισσότερα για τα docstring μπορείτε να βρείτε στην ενότητα Συμβολοσειρές Τεκμηρίωσης.) Υπάρχουν εργαλεία που χρησιμοποιούν docstrings για την αυτόματη παραγωγή διαδικτυακής ή έντυπης τεκμηρίωσης ή για να αφήσουν τον χρήστη να περιηγηθεί διαδραστικά στον κώδικα· είναι καλή πρακτική να συμπεριλαμβάνονται docstrings στον κώδικα που γράφετε, για αυτό κάντε το συνήθεια.

Η εκτέλεση μιας συνάρτησης εισάγει έναν νέο πίνακα συμβόλων που χρησιμοποιείται για τις τοπικές μεταβλητές της συνάρτησης. Πιο συγκεκριμένα, όλες οι εκχωρήσεις μεταβλητών σε μια συνάρτηση αποθηκεύουν την τιμή στον πίνακα τοπικών συμβόλων∙ ενώ οι αναφορές μεταβλητών κοιτάζονται πρώτα στον πίνακα τοπικών συμβόλων, στη συνέχεια στους πίνακες τοπικών συμβόλων των συναρτήσεων που περικλείουν, μετά στον πίνακα καθολικών συμβόλων και, τέλος, στον πίνακα ενσωματωμένων ονομάτων. Έτσι, οι καθολικές μεταβλητές και οι μεταβλητές των συναρτήσεων που περικλείουν δεν μπορούν να εκχωρηθούν ως μια τιμή μέσα σε μια συνάρτηση (εκτός εάν, για καθολικές μεταβλητές, που ονομάζονται σε μια δήλωση `global` ή, για μεταβλητές συναρτήσεων που περικλείουν, ονομάζονται ως μια δήλωση `nonlocal`), αν και μπορεί να αναφέρονται.

Οι πραγματικές παράμετροι (ορίσματα) σε μια κλήση συνάρτησης εισάγονται στον τοπικό πίνακα συμβόλων της καλούμενης συνάρτησης όταν αυτή καλείται· έτσι, τα ορίσματα μεταβιβάζονται χρησιμοποιώντας call by value (όπου η value είναι πάντα ένα αντικείμενο reference, όχι την τιμή του αντικειμένου). [1] Όταν μια συνάρτηση καλεί μια άλλη συνάρτηση ή καλεί τον εαυτό της αναδρομικά, δημιουργείται ένας νέος πίνακας τοπικών συμβόλων για αυτήν την κλήση.

Ένας ορισμός συνάρτησης συσχετίζει το όνομα της συνάρτησης με το αντικείμενο συνάρτησης στον τρέχοντα πίνακα συμβόλων. Ο διερμηνέας αναγνωρίζει το αντικείμενο στο οποίο επισημαίνεται αυτό το όνομα ως συνάρτηση που ορίζεται από τον χρήστη. Άλλα ονόματα μπορούν επίσης να δείχνουν το ίδιο αντικείμενο συνάρτησης και μπορούν επίσης να χρησιμοποιηθούν για πρόσβαση στη συνάρτηση:

    >>> fib
    <function fib at 10042ed0>
    >>> f = fib
    >>> f(100)
    0 1 1 2 3 5 8 13 21 34 55 89

Προερχόμενοι από άλλες γλώσσες, μπορεί να αντιταχθείτε ότι το `fib` δεν είναι μια συνάρτηση αλλά μια διαδικασία, καθώς δεν επιστρέφει μια τιμή. Στην πραγματικότητα, ακόμη και συναρτήσεις χωρίς δήλωση `return` επιστρέφουν μια τιμή, αν και μάλλον βαρετή. Αυτή η τιμή ονομάζεται `None`. Η εγγραφή της τιμής `None` από τον διερμηνέα, εάν θα ήταν η μόνη τιμή που γράφεται. Μπορείτε να το δείτε αν το θέλετε πραγματικά χρησιμοποιώντας τη `print()`:

    >>> fib(0)
    >>> print(fib(0))
    None

Είναι απλό να γράψετε μια συνάρτηση που επιστρέφει μια λίστα με τους αριθμούς της σειράς Fibonacci, αντί να την εκτυπώσετε:

    >>> def fib2(n):  # return Fibonacci series up to n
    ...     """Return a list containing the Fibonacci series up to n."""
    ...     result = []
    ...     a, b = 0, 1
    ...     while a < n:
    ...         result.append(a)    # see below
    ...         a, b = b, a+b
    ...     return result
    ...
    >>> f100 = fib2(100)    # call it
    >>> f100                # write the result
    [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Αυτό το παράδειγμα, ως συνήθως, δείχνει μερικά νέα χαρακτηριστικά Python:

- Η δήλωση `return` επιστρέφει με μια τιμή από μια συνάρτηση. Το `return` χωρίς όρισμα έκφρασης, επιστρέφει το `None`. Η πτώση του τέλους μιας συνάρτησης επιστρέφει επίσης `None`.

- Η δήλωση `result.append(a)` καλεί μια μέθοδο του αντικειμένου της λίστας `result`. Μια μέθοδος είναι μια συνάρτηση που “ανήκει” σε ένα αντικείμενο και ονομάζεται `obj.methodname`, όπου `obj` είναι κάποιο αντικείμενο (αυτό μπορεί να είναι μια έκφραση), και το `methodname` είναι το όνομα μιας μεθόδου που ορίζεται από τον τύπο του αντικειμένου. Διαφορετικοί τύποι ορίζουν διαφορετικές μεθόδους. Μέθοδοι διαφορετικών τύπων μπορεί να έχουν το ίδιο όνομα χωρίς να προκαλούν ασάφεια. (Είναι δυνατό να ορίσετε τους δικούς σας τύπους αντικειμένων και μεθόδους, χρησιμοποιώντας classes, δείτε Κλάσεις) Η μέθοδος `append()` που εμφανίζεται στο παράδειγμα ορίζεται για αντικείμενα λίστας· προσθέτει ένα νέο στοιχείο στο τέλος της λίστας. Σε αυτό το παράδειγμα είναι ισοδύναμο με `result = result + [a]`, αλλά πιο αποτελεσματικό.

## 4.9. Περισσότερο για τον Καθορισμό Συναρτήσεων

Είναι επίσης δυνατός ο ορισμός συναρτήσεων με μεταβλητό αριθμό ορισμάτων. Υπάρχουν τρεις μορφές, που μπορούν να συνδυαστούν.

### 4.9.1. Προεπιλεγμένες Τιμές Ορίσματος

Η πιο χρήσιμη φόρμα είναι να καθορίσετε μια προεπιλεγμένη τιμή για ένα ή περισσότερα ορίσματα. Αυτό δημιουργεί μια συνάρτηση που μπορεί να κληθεί με λιγότερα ορίσματα από αυτά που έχει ορίσει ότι επιτρέπει. Για παράδειγμα:

    def ask_ok(prompt, retries=4, reminder='Please try again!'):
        while True:
            reply = input(prompt)
            if reply in {'y', 'ye', 'yes'}:
                return True
            if reply in {'n', 'no', 'nop', 'nope'}:
                return False
            retries = retries - 1
            if retries < 0:
                raise ValueError('invalid user response')
            print(reminder)

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

- δίνοντας μόνο το υποχρεωτικό όρισμα: `ask_ok('Do you really want to quit?')`

- δίνοντας ένα από τα προαιρετικά ορίσματα: `ask_ok('OK to overwrite the file?', 2)`

- ή δίνοντας όλα τα ορίσματα: `ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')`

Αυτό το παράδειγμα εισάγει επίσης το keyword `in`. Αυτό ελέγχει εάν μια ακολουθία περιέχει ή όχι μια συγκεκριμένη τιμή.

Οι προεπιλεγμένες τιμές αξιολογούνται στο σημείο του ορισμού της συνάρτησης στο πεδίο που ορίζεται, έτσι ώστε

    i = 5

    def f(arg=i):
        print(arg)

    i = 6
    f()

θα εκτυπώσει `5`.

Σημαντική προειδοποίηση: Η προεπιλεγμένη τιμή αξιολογείται μόνο μία φορά. Αυτό κάνει τη διαφορά όταν η προεπιλογή είναι ένα μεταβλητό αντικείμενο, όπως μια λίστα, λεξικό ή στιγμιότυπα των περισσότερων κλάσεων. Για παράδειγμα, η ακόλουθη συνάρτηση συσσωρεύει τα ορίσματα που διαβάζονται σε αυτό σε επόμενες κλήσεις:

    def f(a, L=[]):
        L.append(a)
        return L

    print(f(1))
    print(f(2))
    print(f(3))

Αυτό θα εκτυπώσει

    [1]
    [1, 2]
    [1, 2, 3]

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

    def f(a, L=None):
        if L is None:
            L = []
        L.append(a)
        return L

### 4.9.2. Ορίσματα Keyword

Οι συναρτήσεις μπορούν επίσης να κληθούν χρησιμοποιώντας το keyword arguments της μορφής `kwarg=value`. Για παράδειγμα, την ακόλουθη συνάρτηση:

    def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
        print("-- This parrot wouldn't", action, end=' ')
        print("if you put", voltage, "volts through it.")
        print("-- Lovely plumage, the", type)
        print("-- It's", state, "!")

δέχεται ένα απαιτούμενο όρισμα (`voltage`) και τρία προαιρετικά ορίσματα (`state`, `action`, και `type`). Αυτή η συνάρτηση μπορεί να κληθεί με οποιονδήποτε από τους ακόλουθους τρόπους:

    parrot(1000)                                          # 1 positional argument
    parrot(voltage=1000)                                  # 1 keyword argument
    parrot(voltage=1000000, action='VOOOOOM')             # 2 keyword arguments
    parrot(action='VOOOOOM', voltage=1000000)             # 2 keyword arguments
    parrot('a million', 'bereft of life', 'jump')         # 3 positional arguments
    parrot('a thousand', state='pushing up the daisies')  # 1 positional, 1 keyword

αλλά όλες οι ακόλουθες κλήσεις θα ήταν άκυρες:

    parrot()                     # required argument missing
    parrot(voltage=5.0, 'dead')  # non-keyword argument after a keyword argument
    parrot(110, voltage=220)     # duplicate value for the same argument
    parrot(actor='John Cleese')  # unknown keyword argument

Σε μια κλήση συνάρτησης, τα keyword ορίσματα πρέπει να ακολουθούν ορίσματα θέσης. Όλα τα keyword ορίσματα που διαβάζονται πρέπει να ταιριάζουν με ένα από τα ορίσματα που γίνονται δεκτά από τη συνάρτηση (π.χ. το `actor` δεν είναι έγκυρο όρισμα για τη συνάρτηση `parrot`), και η διάταξη τους δεν είναι σημαντική. Αυτό περιλαμβάνει επίσης μη προαιρετικά ορίσματα (π.χ. `parrot(voltage=1000)` είναι επίσης έγκυρο). Κανένα όρισμα δεν μπορεί να λάβει μια τιμή περισσότερες από μία φορές. Ακολουθεί ένα παράδειγμα που αποτυγχάνει λόγω αυτού του περιορισμού:

    >>> def function(a):
    ...     pass
    ...
    >>> function(0, a=0)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: function() got multiple values for argument 'a'

Όταν υπάρχει μια τελική επίσημη παράμετρος της μορφής `**name`, λαμβάνει ένα λεξικό (δείτε Mapping types — dict, frozendict) που περιέχει όλα τα keyword ορίσματα εκτός από αυτά που αντιστοιχούν σε μια επίσημη παράμετρο. Αυτό μπορεί να συνδυαστεί με μια επίσημη παράμετρος της μορφής `*name` (που περιγράφεται στην επόμενη υποενότητα) η οποία λαμβάνει ένα tuple που περιέχει τα ορίσματα θέσης πέρα από την επίσημη λίστα παραμέτρων. (Το `*name` πρέπει να εμφανίζεται πριν από το `**name`.) Για παράδειγμα, αν ορίσουμε μια συνάρτηση όπως αυτή:

    def cheeseshop(kind, *arguments, **keywords):
        print("-- Do you have any", kind, "?")
        print("-- I'm sorry, we're all out of", kind)
        for arg in arguments:
            print(arg)
        print("-" * 40)
        for kw in keywords:
            print(kw, ":", keywords[kw])

Μπορεί να καλεστεί κάπως έτσι:

    cheeseshop("Limburger", "It's very runny, sir.",
               "It's really very, VERY runny, sir.",
               shopkeeper="Michael Palin",
               client="John Cleese",
               sketch="Cheese Shop Sketch")

και φυσικά θα εκτυπώσει:

    -- Do you have any Limburger ?
    -- I'm sorry, we're all out of Limburger
    It's very runny, sir.
    It's really very, VERY runny, sir.
    ----------------------------------------
    shopkeeper : Michael Palin
    client : John Cleese
    sketch : Cheese Shop Sketch

Λάβετε υπόψη ότι η σειρά με την οποία εκτυπώνονται τα keyword ορίσματα είναι εγγυημένη ότι ταιριάζει με τη σειρά με την οποία δόθηκαν στην κλήση της συνάρτησης.

### 4.9.3. Ειδικές παράμετροι

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

Ένας ορισμός συνάρτησης μπορεί να μοιάζει με αυτό:

    def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
          -----------    ----------     ----------
            |             |                  |
            |        Positional or keyword   |
            |                                - Keyword only
             -- Positional only

όπου τα `/` και `*` είναι προαιρετικά. Εάν χρησιμοποιούνται, αυτά τα σύμβολα υποδεικνύουν το είδος της παραμέτρους με τον τρόπο που τα ορίσματα μπορούν να μεταβιβαστούν στη συνάρτηση: μόνο θέσης (positional-only), θέσης ή keyword (positional-or-keyword), και μόνο keyword (keyword-only). Οι keyword παράμετροι αναφέρονται επίσης ως ονομασμένες παράμετροι.

#### 4.9.3.1. Παράμετροι Θέσης ή Keyword

Εάν τα `/` και `*` δεν υπάρχουν στον ορισμό της συνάρτησης, τα ορίσματα μπορούν να μεταβιβαστούν σε μια συνάρτηση ανά θέση ή κατά keyword.

#### 4.9.3.2. Παράμετροι Μόνο-Θέσης

Επανεξετάζοντας αυτό το θέμα λίγο πιο λεπτομερώς, είναι δυνατό να επισημάνετε ορισμένες παραμέτρους ως μόνο θέσης.Εάν μόνο θέσης, η σειρά των παραμέτρων έχει σημασία και οι παράμετροι δεν μπορούν να μεταβιβαστούν με keyword. Οι παράμετροι μόνο θέσης τοποθετούνται πριν από ένα `/` (προς τα εμπρός-κάθετος). Το `/` χρησιμοποιείται για να διαχωρίσει λογικά τις παραμέτρους μόνο θέσης από τις υπόλοιπες παραμέτρους. Εάν δεν υπάρχει το `/` στον ορισμό της συνάρτησης, δεν υπάρχουν παράμετροι μόνο θέσης.

Οι παράμετροι που ακολουθούν το `/` μπορεί να είναι θέσης ή keyword ή μόνο keyword.

#### 4.9.3.3. Ορίσματα μόνο Keyword

Για να επισημάνετε τις παραμέτρους ως μόνο keyword, υποδεικνύοντας ότι οι παράμετροι πρέπει να περάσουν από το keyword όρισμα, τοποθετήσετε ένα `*` στη λίστα ορισμάτων ακριβώς πριν από την πρώτη παράμετρο μόνο keyword.

#### 4.9.3.4. Παραδείγματα Συναρτήσεων

Σκεφτείτε τα ακόλουθα παραδείγματα ορισμών συναρτήσεων δίνοντας ιδιαίτερη προσοχή στους δείκτες `/` και `*`:

    >>> def standard_arg(arg):
    ...     print(arg)
    ...
    >>> def pos_only_arg(arg, /):
    ...     print(arg)
    ...
    >>> def kwd_only_arg(*, arg):
    ...     print(arg)
    ...
    >>> def combined_example(pos_only, /, standard, *, kwd_only):
    ...     print(pos_only, standard, kwd_only)

Ο πρώτος ορισμός συνάρτησης, `standard_arg`, η πιο οικεία μορφή, δεν θέτει περιορισμούς στη σύμβαση κλήσης και τα ορίσματα μπορούν να περάσουν από θέση ή από keyword:

    >>> standard_arg(2)
    2

    >>> standard_arg(arg=2)
    2

Η δεύτερη συνάρτηση `pos_only_arg` περιορίζεται στη χρήση μόνο παραμέτρων θέσης καθώς υπάρχει ένα `/` στον ορισμός της συνάρτησης:

    >>> pos_only_arg(1)
    1

    >>> pos_only_arg(arg=1)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'

Η τρίτη συνάρτηση `kwd_only_arg` επιτρέπει μόνο keyword ορίσματα όπως υποδεικνύεται από ένα `*` στον ορισμός της συνάρτησης:

    >>> kwd_only_arg(3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given

    >>> kwd_only_arg(arg=3)
    3

Και το τελευταίο χρησιμοποιεί και τις τρεις συμβάσεις κλήσης στον ίδιο ορισμό συνάρτησης:

    >>> combined_example(1, 2, 3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: combined_example() takes 2 positional arguments but 3 were given

    >>> combined_example(1, 2, kwd_only=3)
    1 2 3

    >>> combined_example(1, standard=2, kwd_only=3)
    1 2 3

    >>> combined_example(pos_only=1, standard=2, kwd_only=3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: combined_example() got some positional-only arguments passed as keyword arguments: 'pos_only'

Τέλος, εξετάστε αυτόν τον ορισμό συνάρτησης που έχει μια πιθανή σύγκρουση μεταξύ του ορίσματος θέσης `name` και `**kwds` που έχει ως κλειδί το `name`:

    def foo(name, **kwds):
        return 'name' in kwds

Δεν υπάρχει καμία πιθανή κλήση που θα την κάνει να επιστρέψει `True` καθώς το keyword `'name'` θα συνδέεται πάντα με την πρώτη παράμετρο. Για παράδειγμα:

    >>> foo(1, **{'name': 2})
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: foo() got multiple values for argument 'name'
    >>>

Αλλά χρησιμοποιώντας `/` (ορίσματα μόνο θέσης), είναι δυνατό καθώς επιτρέπει το `name` ως όρισμα θέσης και το `'name'` ως κλειδί στα keyword ορίσματα:

    >>> def foo(name, /, **kwds):
    ...     return 'name' in kwds
    ...
    >>> foo(1, **{'name': 2})
    True

Με άλλα λόγια, τα ονόματα των παραμέτρων μόνο θέσης μπορούν να χρησιμοποιηθούν σε `**kwds` χωρίς ασάφεια.

#### 4.9.3.5. Ανακεφαλαίωση

Η περίπτωση χρήσης θα καθορίσει ποιες παραμέτρους θα χρησιμοποιηθούν στον ορισμό της συνάρτησης:

    def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):

Ως καθοδήγηση:

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

- Χρησιμοποιήστε keyword μόνο όταν τα ονόματα έχουν νόημα και ο ορισμός της συνάρτησης είναι πιο κατανοητός όταν είναι ρητός με ονόματα ή θέλετε να αποτρέψετε τους χρήστες να βασίζονται στη θέση του επιχειρήματος που μεταβιβάζεται.

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

### 4.9.4. Λίστες Αυθαίρετων Ορισμάτων

Τέλος, η λιγότερο συχνά χρησιμοποιούμενη επιλογή είναι να ορίσετε ότι μια συνάρτηση μπορεί να κληθεί με έναν αυθαίρετο αριθμός ορισμάτων. Αυτά τα ορίσματα θα τυλιχθούν σε μια πλειάδα (tuple) (βλ. Πλειάδες (Tuples) και Ακολουθίες). Πριν από τον μεταβλητό αριθμό ορισμάτων ενδέχεται να προκύψουν μηδέν ή περισσότερα κανονικά ορίσματα.

    def write_multiple_items(file, separator, *args):
        file.write(separator.join(args))

Κανονικά, αυτά τα variadic ορίσματα θα είναι τελευταία στη λίστα των επίσημων παραμέτρων, επειδή συλλέγουν όλα τα υπόλοιπα ορίσματα εισόδου που μεταβιβάζονται στη συνάρτηση. Οποιεσδήποτε τυπικές παράμετροι που εμφανίζονται μετά την παράμετρο `*args` είναι “μόνο keyword” ορίσματα, που σημαίνει ότι μπορούν να χρησιμοποιηθούν μόνο ως λέξεις-κλειδιά και όχι ως ορίσματα θέσης.

    >>> def concat(*args, sep="/"):
    ...     return sep.join(args)
    ...
    >>> concat("earth", "mars", "venus")
    'earth/mars/venus'
    >>> concat("earth", "mars", "venus", sep=".")
    'earth.mars.venus'

### 4.9.5. Unpacking Λίστες Ορισμάτων

Η αντίστροφη κατάσταση συμβαίνει όταν τα ορίσματα βρίσκονται ήδη σε μια λίστα ή πλειάδα (tuple), αλλά πρέπει να αποσυμπιεστούν για μια κλήση συνάρτησης που απαιτεί ξεχωριστά ορίσματα θέσης. Για παράδειγμα, η ενσωματωμένη (built-in) συνάρτηση `range()` αναμένει ξεχωριστά start και stop ορίσματα. Εάν δεν είναι διαθέσιμα ξεχωριστά, γράψτε την κλήση συνάρτησης με τον `*`-τελεστή για να αποσυμπιέσετε τα ορίσματα από μια λίστα ή πλειάδα (tuple):

    >>> list(range(3, 6))            # normal call with separate arguments
    [3, 4, 5]
    >>> args = [3, 6]
    >>> list(range(*args))            # call with arguments unpacked from a list
    [3, 4, 5]

Με τον ίδιο τρόπο, τα λεξικά μπορούν να παραδίδουν keyword ορίσματα με τον `**`-τελεστή:

    >>> def parrot(voltage, state='a stiff', action='voom'):
    ...     print("-- This parrot wouldn't", action, end=' ')
    ...     print("if you put", voltage, "volts through it.", end=' ')
    ...     print("E's", state, "!")
    ...
    >>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
    >>> parrot(**d)
    -- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

### 4.9.6. Εκφράσεις Lambda

Μπορούν αν δημιουργηθούν μικρές ανώνυμες συναρτήσεις με το keyword `lambda`. Αυτή η συνάρτηση επιστρέφει το άθροισμα των δύο ορισμάτων της: `lambda a, b: a+b`. Οι συναρτήσεις Lambda μπορούν να χρησιμοποιηθούν όπου απαιτούνται αντικείμενα συνάρτησης. Περιορίζονται συντακτικά σε μία μόνο έκφραση. Σημασιολογικά, είναι απλώς syntactic sugar για έναν ορισμό κανονικής συνάρτησης. Όπως οι ορισμοί ένθετων συναρτήσεων, οι συναρτήσεις lambda μπορούν να παραπέμπουν σε μεταβλητές από το πεδίο που περιέχει:

    >>> def make_incrementor(n):
    ...     return lambda x: x + n
    ...
    >>> f = make_incrementor(42)
    >>> f(0)
    42
    >>> f(1)
    43

Το παραπάνω παράδειγμα χρησιμοποιεί μια έκφραση lambda για να επιστρέψει μια συνάρτηση. Μια άλλη χρήση είναι η μετάδοση μιας μικρής συνάρτησης ως όρισμα. Για παράδειγμα, η `list.sort()` δέχεται μια συνάρτηση ταξινόμησης κλειδιού key η οποία μπορεί να είναι μια συνάρτηση lambda:

    >>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
    >>> pairs.sort(key=lambda pair: pair[1])
    >>> pairs
    [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]

### 4.9.7. Συμβολοσειρές Τεκμηρίωσης

Ακολουθούν ορισμένες συμβάσεις σχετικά με το περιεχόμενο και τη μορφοποίηση των συμβολοσειρών τεκμηρίωσης.

Η πρώτη γραμμή πρέπει να είναι πάντα μια σύντομη, συνοπτική περίληψη του σκοπού του αντικειμένου. Για συντομία, δεν πρέπει να αναφέρει ρητά το όνομα ή τον τύπο του αντικειμένου , καθώς αυτά είναι διαθέσιμα με άλλα μέσα (εκτός εάν το όνομα είναι ρήμα που περιγράφει τη λειτουργία της συνάρτησης). Αυτή η γραμμή πρέπει να ξεκινά με κεφαλαίο γράμμα και να τελειώνει με τελεία.

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

The Python parser strips indentation from multi-line string literals when they serve as module, class, or function docstrings.

Ακολουθεί ένα παράδειγμα ενός πολλαπλών γραμμών docstring:

    >>> def my_function():
    ...     """Do nothing, but document it.
    ...
    ...     No, really, it doesn't do anything:
    ...
    ...         >>> my_function()
    ...         >>>
    ...     """
    ...     pass
    ...
    >>> print(my_function.__doc__)
    Do nothing, but document it.

    No, really, it doesn't do anything:

        >>> my_function()
        >>>

### 4.9.8. Annotations Συναρτήσεων

Το Function annotations είναι εντελώς προαιρετικές πληροφορίες μεταδεδομένων σχετικά με τους τύπους χρησιμοποιούνται από συναρτήσεις που καθορίζονται από το χρήστη (δείτε PEP 3107 και PEP 484 για περισσότερες πληροφορίες).

Το Annotations αποθηκεύονται στο χαρακτηριστικό `__annotations__` της συνάρτησης ως λεξικό και δεν έχουνε καμία επίδραση σε κανένα άλλο μέρος της συνάρτησης. Τα annotations των παραμέτρων ορίζονται με άνω και κάτω τελεία μετά το όνομα παραμέτρου, ακολουθούμενη από μια έκφραση που αξιολογεί την τιμή του annotation. Τα annotations επιστροφής ορίζονται από ένα literal `->`, ακολουθούμενο από μια έκφραση, μεταξύ της λίστας παραμέτρων και της άνω και κάτω τελείας που υποδηλώνει το τέλος της δήλωσης `def`. Το ακόλουθο παράδειγμα έχει ένα απαιτούμενο όρισμα, ένα προαιρετικό όρισμα και την επιστρεφόμενη τιμή σε annotations:

    >>> def f(ham: str, eggs: str = 'eggs') -> str:
    ...     print("Annotations:", f.__annotations__)
    ...     print("Arguments:", ham, eggs)
    ...     return ham + ' and ' + eggs
    ...
    >>> f('spam')
    Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
    Arguments: spam eggs
    'spam and eggs'

## 4.10. Intermezzo: Στυλ Κώδικα

Now that you are about to write longer, more complex pieces of Python, it is a good time to talk about coding style. Most languages can be written (or more concisely, formatted) in different styles; some are more readable than others. Making it easy for others to read your code is always a good idea, and adopting a nice coding style helps tremendously for that.

Για την Python, το PEP 8 έχει αναδειχθεί ως οδηγός στυλ στον οποίο τηρούν τα περισσότερα έργα· προωθεί ένα πολύ ευανάγνωστο και ευχάριστο στυλ κώδικα. Κάθε προγραμματιστής Python θα πρέπει να το διαβάσει κάποια στιγμή· εδώ είναι τα πιο σημαντικό σημεία που εξάγονται για εσάς:

- Χρησιμοποιήστε εσοχή 4 διαστημάτων και όχι tabs.

  Τα 4 κενά είναι ένας καλός συμβιβασμός μεταξύ της μικρής εσοχής (επιτρέπει μεγαλύτερο βάθος εμφώλευσης) και της μεγάλης εσοχής (ευκολότερη στην ανάγνωση). Τα tabs δημιουργούν σύγχυση, και είναι καλύτερο να παραμείνουν απέξω.

- Τυλίξτε τις γραμμές έτσι ώστε να μην υπερβαίνουν τους 79 χαρακτήρες.

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

- Χρησιμοποιείστε κενές γραμμές για να διαχωρίσετε συναρτήσεις και κλάσεις και μεγαλύτερα μπλοκ κώδικα μέσα συναρτήσεις.

- Όταν είναι δυνατόν, βάλτε σχόλια σε μια δική τους γραμμή.

- Χρησιμοποιήστε docstrings.

- Χρησιμοποιήστε κενά γύρω από τελεστές και μετά από κόμματα, αλλά όχι απευθείας μέσα δε δομές αγκύλων: `a = f(1, 2) + g(3, 4)`.

- Ονομάστε τις κλάσεις και τις συναρτήσεις σας με συνέπεια· η σύμβαση είναι να χρησιμοποιείτε `UpperCamelCase` για τις κλάσεις και `lowercase_with_underscores` για τις συναρτήσεις και τις μεθόδους. Χρησιμοποιείτε πάντα το `self` ως όνομα για το πρώτο όρισμα μεθόδου (δείτε Μια πρώτη ματιά στις Κλάσεις για περισσότερα σχετικά με τις κλάσεις και τις μεθόδους).

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

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

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

[1]

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