# Συχνές ερωτήσεις βιβλιοθήκης και επέκτασης

## Γενικές Ερωτήσεις Βιβλιοθήκης

### Πως μπορώ να βρω ένα module ή μια εφαρμογή για να εκτελέσω την εργασία Χ;

Ελέγξτε την the Library Reference για να δείτε αν υπάρχει ένα σχετικό τυπικό module. (Τελικά θα μάθετε τι υπάρχει στην τυπική βιβλιοθήκη και θα μπορείτε να παραλείψετε αυτό το βήμα.)

Για πακέτα τρίτων, πραγματοποιήστε αναζήτηση στο Python Package Index ή δοκιμάστε στο Google ή σε κάποια άλλη μηχανή αναζήτησης ιστού. Η αναζήτηση με τον όρο «Python» μαζί με μια ή δύο λέξεις κλειδιά για το θέμα που σας ενδιαφέρει συνήθως θα βγάλει κάποιο χρήσιμο αποτέλεσμα.

### Που βρίσκεται το πηγαίο αρχείο math.py (socket.py, regex.py, κ.λπ.);

Εάν δεν μπορείτε να βρείτε ένα πηγαίο αρχείο για ένα module, μπορεί να είναι ένα ενσωματωμένο (built-in) module ή ένα module που φορτώνεται δυναμικά υλοποιημένο σε C, C++ ή άλλη μεταγλωττισμένη γλώσσα. Σε αυτή την περίπτωση μπορεί να μην έχετε το αρχείο προέλευσης ή μπορεί να είναι κάτι σαν `mathmodule.c`, κάπου σε έναν κατάλογο προέλευσης C (όχι στην διαδρομή της Python).

Υπάρχουν (το λιγότερο) τριών ειδών modules στην Python:

1. modules γραμμένα σε Python (.py):

2. modules γραμμένα σε C και φορτώνονται δυναμικά (.dll, .pyd, .so, .sl, κ.λπ.):

3. modules γραμμένα σε C και συνδεδεμένα με τον διερμηνέα: για να λάβετε μια λίστα από αυτά, πληκτρολογήστε:

       import sys
       print(sys.builtin_module_names)

### Πως δημιουργώ ένα εκτελέσιμο Python script στο Unix;

Θα πρέπει να κάνετε δύο πράγματα: η λειτουργία του script αρχείου πρέπει να είναι εκτελέσιμη και η πρώτη γραμμή θα πρέπει αν ξεκινά με `#!` ακολουθούμενη από τη διαδρομή του διερμηνέα της Python.

Το πρώτο γίνεται με την εκτέλεση του `chmod +x scriptfile` ή ίσως `chmod 755 scriptfile`.

Το δεύτερο μπορεί να γίνει με ποικίλους τρόπους. Ο πιο απλός τρόπος είναι να γράψετε

    #!/usr/local/bin/python

ως η πρώτη γραμμή του αρχείου σας, χρησιμοποιώντας το pathname για το πού είναι εγκατεστημένος ο διερμηνέας Python στην πλατφόρμα σας.

Εάν θέλετε το script να είναι ανεξάρτητο, από το που ζει ο διερμηνέας της Python, μπορείτε να χρησιμοποιήσετε το πρόγραμμα env. Σχεδόν όλες οι παραλλαγές Unix υποστηρίζουν τα ακόλουθα, υποθέτοντας ότι ο διερμηνέας Python βρίσκεται σε έναν κατάλογο στο `PATH` του χρήστη:

    #!/usr/bin/env python

Μην το κάνετε αυτό για CGI scripts. Η μεταβλητή `PATH` για τα CGI scripts είναι συχνά πολύ ελάχιστη, επομένως πρέπει να χρησιμοποιήσετε το πραγματικό απόλυτο όνομα διαδρομής του διερμηνέα.

Περιστασιακά, το περιβάλλον ενός χρήστη είναι τόσο γεμάτο που το πρόγραμμα /usr/bin/env αποτυγχάνει ή δεν υπάρχει καθόλου πρόγραμμα env. Σε αυτήν την περίπτωση, μπορείτε να δοκιμάσετε το ακόλουθο hack (λόγω του Alex Rezinsky):

    #! /bin/sh
    """:"
    exec python $0 ${1+"$@"}
    """

Το, ελάχιστης σημασίας, μειονέκτημα είναι ότι αυτό καθορίζει τη συμβολοσειρά __doc__ του script. Ωστόσο, μπορείτε να το διορθώσετε προσθέτοντας

    __doc__ = """...Οτιδήποτε..."""

### Υπάρχει ένα curses/termcap πακέτο για την Python;

Για παραλλαγές Unix: Η τυπική διανομή του πηγαίου της Python συνοδεύεται από ένα curses module στον υποκατάλογο Modules, αν και δεν έχει μεταγλωττιστεί από προεπιλογή. (Λάβετε υπόψη ότι αυτό δεν είναι διαθέσιμο στη διανομή των Windows – δεν υπάρχει curses module για Windows.)

Το module `curses` υποστηρίζει βασικές curses λειτουργίες καθώς και πολλές πρόσθετες λειτουργίες από ncurses και SYSV curses, όπως χρώμα, υποστήριξη εναλλακτικού συνόλου χαρακτήρων, pads και υποστήριξη ποντικιού. Αυτό σημαίνει ότι το module δεν είναι συμβατό με λειτουργικά συστήματα που έχουνε μόνο BSD curses, αλλά δεν φαίνεται να υπάρχουν επί του παρόντος διατηρημένα λειτουργικά συστήματα που να εμπίπτουν σε αυτήν την κατηγορία.

### Υπάρχει κάτι ισοδύναμο με την onexit() της C στην Python;

Το module `atexit` παρέχει μια συνάρτηση καταχωρητή παρόμοια με τη συνάρτηση `onexit()` της C.

### Γιατί δεν λειτουργούν οι χειριστές σήματος μου;

Το πιο συνηθισμένο πρόβλημα είναι ότι ο χειριστής σήματος δηλώνεται με λάθος λίστα ορισμάτων. Ονομάζεται ως

    handler(signum, frame)

οπότε θα πρέπει να δηλωθεί με δύο παραμέτρους:

    def handler(signum, frame):
        ...

## Κοινές εργασίες

### Πως μπορώ να τεστάρω ένα Python πρόγραμμα ή ένα Python component;

Η Python συνοδεύεται από δύο frameworks για τεστ. To module `doctest` βρίσκει παραδείγματα στα docstrings για ένα module και τα εκτελεί, συγκρίνοντας την έξοδο με την αναμενόμενη έξοδο που δίνεται στο docstring.

Το module `unittest` είναι ένα πιο εντυπωσιακό framework που έχει σχεδιαστεί με βάση τα frameworks για τεστ της Java και του Smalltalk.

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

Η «παγκόσμια κύρια λογική» του προγράμματος σας μπορεί να είναι τόσο απλή όσο

    if __name__ == "__main__":
        main_logic()

στο κάτω μέρος του κύριου module του προγράμματος σας.

Από τη στιγμή που το πρόγραμμα σας οργανωθεί ως μια συλλογή από συναρτήσεις και κλάσεις που υλοποιούν μια διαδικασία, θα πρέπει να γράψετε τεστ συναρτήσεις που εκτελούν τις αντίστοιχες διαδικασίες. Μια τεστ σουίτα που αυτοματοποιεί μια ακολουθία από tests μπορεί να συσχετιστεί με κάθε module. Αυτό ακούγεται σαν πολλή δουλειά, αλλά επειδή η Python είναι τόσο σύντομη και ευέλικτη, αυτό είναι εκπληκτικά εύκολο. Μπορείτε να κάνετε την συγγραφή κώδικα πολύ πιο ευχάριστη και διασκεδαστική γράφοντας τις δοκιμαστικές συναρτήσεις σας παράλληλα με τον «κώδικα παραγωγής», καθώς αυτό διευκολύνει την εύρεση σφαλμάτων και ακόμη και ελαττώματα σχεδιασμού νωρίτερα.

«Modules υποστήριξης» που δεν προορίζονται να είναι το κύριο module ενός προγράμματος μπορεί να περιλαμβάνουν αυτοέλεγχο της ενότητας:

    if __name__ == "__main__":
        self_test()

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

### Πως δημιουργώ τεκμηρίωση από doc strings;

Το module `pydoc` μπορεί να δημιουργήσει HTML από τα doc strings στον πηγαίο κώδικα Python σας. Μια εναλλακτική λύση για τη δημιουργία μιας τεκμηρίωσης API αποκλειστικά με docstrings είναι το epydoc. Sphinx μπορεί επίσης να περιλαμβάνει docstring περιεχόμενο.

### Πως μπορώ να λάβω μόνο ένα πάτημα κουμπιού κάθε φορά;

Για τις παραλλαγές του Unix υπάρχουν πολλές λύσεις. Είναι απλό να κάνετε αυτό χρησιμοποιώντας curses, αλλά τα curses είναι ένα αρκετά μεγάλο module για εκμάθηση.

## Νήματα (Threads)

### Πώς μπορώ να προγραμματίσω χρησιμοποιώντας νήματα(threads);

Βεβαιωθείτε ότι χρησιμοποιείτε το module `threading` και όχι το module `_thread`. Το module `threading` παρέχει ένα πιο βολικό και υψηλού επιπέδου τρόπο διαχείρισης νημάτων, χτίζοντας πάνω στις βασικές λειτουργίες του module `_thread`.

### Κανένα από τα νήματα (threads) μου δεν φαίνεται να εκτελείται: γιατί;

Μόλις γίνει έξοδος από το κύριο νήμα, τερματίζονται όλα τα νήματα. Το κύριο νήμα τρέχει πολύ γρήγορα, χωρίς να δίνεται χρόνος στα νήματα να κάνουν οποιαδήποτε εργασία.

Μια απλή λύση είναι να προσθέσετε ένα sleep στο τέλος που προγράμματος που είναι αρκετό για να τερματιστούν όλα τα νήματα:

    import threading, time

    def thread_task(name, n):
        for i in range(n):
            print(name, i)

    for i in range(10):
        T = threading.Thread(target=thread_task, args=(str(i), i))
        T.start()

    time.sleep(10)  # <---------------------------!

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

Μια απλή λύση είναι να προσθέσετε ένα μικρό sleep στην αρχή της λειτουργίας εκτέλεσης:

    def thread_task(name, n):
        time.sleep(0.001)  # <--------------------!
        for i in range(n):
            print(name, i)

    for i in range(10):
        T = threading.Thread(target=thread_task, args=(str(i), i))
        T.start()

    time.sleep(10)

Αντί να προσπαθείτε να μαντέψετε μια καλή τιμή καθυστέρησης για την `time.sleep()`, είναι καλύτερα να χρησιμοποιείτε κάποιο είδος μηχανισμού σηματοφόρου. Μια ιδέα είναι να χρησιμοποιήσετε το module `queue` για να δημιουργήσετε ένα αντικείμενο ουράς, αφήνοντας το κάθε νήμα να προσθέτει ένα token στην ουρά όταν ολοκληρωθεί, και να αφήνει στο κύριο νήμα να διαβάσει τόσα tokens από την ουρά όσα είναι και τα νήματα.

### Πως μπορώ να μοιράσω την εργασία σε ένα σωρό από νήματα για αυτήν την εργασία;

Ο πιο εύκολος τρόπος είναι να χρησιμοποιήσετε το module `concurrent.futures`, ειδικά την κλάση `ThreadPoolExecutor`.

Ή, ένα θέλετε καλό έλεγχο στον αλγόριθμο αποστολής, μπορείτε να γράψετε τη δική σας λογική με μη αυτόματο τρόπο. Χρησιμοποιήστε το module `queue` για να δημιουργήσετε μια ουρά που περιέχει μια λίστα εργασιών. Η κλάση `Queue` διατηρεί μια λίστα αντικειμένων και έχει μια μέθοδο `.put(obj)` που προσθέτει στοιχεία στην ουρά και μια μέθοδο `.get()` για επιστροφή. Η κλάση θα φροντίσει για το κλείδωμα που είναι απαραίτητο για να διασφαλίσει ότι κάθε εργασία θα παραδοθεί ακριβώς μία φορά.

Ένα απλό παράδειγμα:

    import threading, queue, time

    # The worker thread gets jobs off the queue.  When the queue is empty, it
    # assumes there will be no more work and exits.
    # (Realistically workers will run until terminated.)
    def worker():
        print('Running worker')
        time.sleep(0.1)
        while True:
            try:
                arg = q.get(block=False)
            except queue.Empty:
                print('Worker', threading.current_thread(), end=' ')
                print('queue empty')
                break
            else:
                print('Worker', threading.current_thread(), end=' ')
                print('running with argument', arg)
                time.sleep(0.5)

    # Create queue
    q = queue.Queue()

    # Start a pool of 5 workers
    for i in range(5):
        t = threading.Thread(target=worker, name='worker %i' % (i+1))
        t.start()

    # Begin adding work to the queue
    for i in range(50):
        q.put(i)

    # Give threads time to run
    print('Main thread sleeping')
    time.sleep(5)

Κατά την εκτέλεση, αυτό θα παράγει την ακόλουθη έξοδο:

    Running worker
    Running worker
    Running worker
    Running worker
    Running worker
    Main thread sleeping
    Worker <Thread(worker 1, started 130283832797456)> running with argument 0
    Worker <Thread(worker 2, started 130283824404752)> running with argument 1
    Worker <Thread(worker 3, started 130283816012048)> running with argument 2
    Worker <Thread(worker 4, started 130283807619344)> running with argument 3
    Worker <Thread(worker 5, started 130283799226640)> running with argument 4
    Worker <Thread(worker 1, started 130283832797456)> running with argument 5
    ...

Συμβουλευτείτε την τεκμηρίωση του module για περισσότερες λεπτομέρειες· η κλάση `Queue` παρέχει μια χαρακτηριστική διεπαφή.

### Ποια είδη global μετάλλαξης τιμής είναι ασφαλή για νήμα(thread);

Ένα global interpreter lock (GIL) χρησιμοποιείται εσωτερικά για να διασφαλιστεί ότι μόνο ένα νήμα εκτελείται στο Python VM κάθε φορά. Γενικά, η Python προσφέρει εναλλαγή μεταξύ νημάτων μόνο μεταξύ εντολών bytecode· το πόσο συχνά γίνεται αυτή η εναλλαγή καθορίζεται από την `sys.setswitchinterval()`. Κάθε εντολή bytecode και επομένως όλος ο κώδικας υλοποίησης C που φτάνει από κάθε εντολή είναι επομένως ατομικός από την άποψη ενός προγράμματος Python.

Θεωρητικά, αυτό σημαίνει ότι μια ακριβώς λογιστική απαιτεί ακριβή κατανόηση της εφαρμογής bytecode PVM. Στην πράξη, σημαίνει ότι οι λειτουργίες σε κοινόχρηστες μεταβλητές ενσωματωμένων δεδομένων (ακέραιοι, λίστες, λεξικά, κ.λπ.) που «φαίνονται ατομικά» πραγματικά είναι.

Για παράδειγμα, οι ακόλουθες πράξεις είναι όλες ατομικές (L, L1, L2 είναι λίστες, D, D1, D2 είναι λεξικά, x, y είναι αντικείμενα, i, j είναι ακέραιοι):

    L.append(x)
    L1.extend(L2)
    x = L[i]
    x = L.pop()
    L1[i:j] = L2
    L.sort()
    x = y
    x.field = y
    D[x] = y
    D1.update(D2)
    D.keys()

Αυτά δεν είναι:

    i = i+1
    L.append(L[-1])
    L[i] = L[j]
    D[x] = D[x] + 1

Οι λειτουργίες που αντικαθιστούν άλλα αντικείμενα ενδέχεται να καλέσουν αυτά τα άλλα αντικείμενα της `__del__()` όταν ο αριθμός αναφοράς τους φτάσει στο μηδέν και αυτό μπορεί να επηρεάσει πράγματα. Αυτό ισχύει ιδιαίτερα για τις μαζικές ενημερώσεις σε λεξικά και λίστες. Σε περίπτωση αμφιβολίας, χρησιμοποιήστε ένα mutex!

### Μπορούμε να απαλλαγούμε από ένα Global Interpreter Lock;

Το global interpreter lock (GIL) θεωρείται συχνά ως εμπόδιο για την ανάπτυξη της Python σε υψηλής τεχνολογίας μηχανήματα διακομιστών με πολυεπεξεργαστές, επειδή ένα πρόγραμμα Python με πολλά νήματα χρησιμοποιεί αποτελεσματικά μόνο μία CPU, λόγω της επιμονής ότι (σχεδόν) όλος ο κώδικας της Python μπορεί να εκτελεστεί μόνο όσο διατηρείται το GIL.

Με την έγκριση του PEP 703 έχει ξεκινήσει η εργασία για την αφαίρεση του GIL από την υλοποίηση CPython της Python. Αρχικά θα υλοποιηθεί ως μια προαιρετική σημαία μεταγλωττιστή κατά την κατασκευή του διερμηνευτή, και έτσι θα είναι διαθέσιμες ξεχωριστές κατασκευές με και χωρίς το GIL. Μακροπρόθεσμα, η ελπίδα είναι να καταλήξουμε σε μια ενιαία κατασκευή, μόλις οι επιπτώσεις στην απόδοση της αφαίρεσης του GIL να γίνουν πλήρως κατανοητές. Η Python 3.13 είναι πιθανό να είναι η πρώτη έκδοση που θα περιέχει αυτήν την εργασία, αν και μπορεί να μην είναι πλήρως λειτουργική σε αυτήν την έκδοση.

Η τρέχουσα εργασία για την αφαίρεση του GIL βασίζεται σε ένα fork της Python 3.9 με το GIL αφαιρεμένο από τον Sam Gross. Πριν από αυτό, στις ημέρες της Python 1.5, ο Greg Stein υλοποίησε στην πραγματικότητα ένα ολοκληρωμένο σύνολο διορθώσεων (τα «free threading» patches) που αφαίρεσαν το GIL και το αντικατέστησαν με λεπτομερή κλείδωμα. Ο Adam Olsen έκανε ένα παρόμοιο πείραμα στο έργο του python-safethread. Δυστυχώς, και τα δύο αυτά προηγούμενα πειράματα παρουσίασαν απότομη πτώση στην απόδοση ενός νήματος (τουλάχιστον 30% πιο αργά), λόγω της ποσότητας λεπτομερούς κλειδώματος που απαιτείται για να αντισταθμιστεί η αφαίρεση του GIL. Το fork της Python 3.9 είναι η πρώτη προσπάθεια αφαίρεσης του GIL με αποδεκτή επίδραση στην απόδοση.

Η παρουσία του GIL στις τρέχουσες εκδόσεις της Python δεν σημαίνει ότι δεν μπορείτε να κάνετε καλή χρήση της Python σε μηχανές με πολλαπλές CPU! Απλώς πρέπει να είστε δημιουργικοί με τον διαχωρισμό της εργασίας μεταξύ πολλαπλών processes αντί για πολλαπλά threads. Η κλάση `ProcessPoolExecutor` στο νέο module `concurrent.futures` παρέχει έναν εύκολο τρόπο για να το κάνετε αυτό· το module `multiprocessing` παρέχει μια χαμηλότερου επιπέδου API σε περίπτωση που θέλετε περισσότερο έλεγχο στην αποστολή εργασιών.

Η συνετή χρήση των επεκτάσεων C βοηθάει επίσης· εάν χρησιμοποιείτε μια επέκταση C για να εκτελέσετε μια χρονοβόρα εργασία, η επέκταση μπορεί να απελευθερώσει το GIL ενώ το νήμα εκτέλεσης βρίσκεται στον κώδικα C και να επιτρέψει σε άλλα νήματα να ολοκληρώσουν κάποια εργασία. Ορισμένα τυπικά module βιβλιοθήκης όπως `zlib` και `hashlib` το κάνουν ήδη αυτό.

Μια εναλλακτική προσέγγιση για τη μείωση της επίδρασης του GIL είναι να γίνει το GIL ένα κλείδωμα ανά κατάσταση διερμηνευτή αντί για πραγματικά παγκόσμιο. Αυτό υλοποιήθηκε πρώτα στην Python 3.12 και είναι διαθέσιμο στο C API. Μια διεπαφή Python σε αυτό αναμένεται στην Python 3.13. Ο κύριος περιορισμός προς το παρόν είναι πιθανό να είναι τα 3rd party extension modules, καθώς αυτά πρέπει να γράφονται με πολλαπλούς διερμηνευτές στο μυαλό για να είναι χρησιμοποιήσιμα, οπότε πολλά παλαιότερα extension modules δεν θα είναι χρησιμοποιήσιμα.

## Είσοδος και Έξοδος

### Πώς μπορώ να διαγράψω ένα αρχείο; (Και άλλες ερωτήσεις για το αρχείο…)

Χρησιμοποιήστε τα `os.remove(filename)` ή `os.unlink(filename)` ∙ για τεκμηρίωση, ανατρέξτε στο module `os`. Οι δύο συναρτήσεις είναι ίδιες∙ η `unlink()` είναι απλώς το όνομα της κλήσης συστήματος Unix για αυτήν τη λειτουργία.

Για να καταργήσετε έναν κατάλογο, χρησιμοποιήστε τη `os.rmdir()` ∙ χρησιμοποιήστε τη `os.mkdir()` για να δημιουργήσετε έναν. Το `os.makedirs(path)` θα δημιουργήσει τυχόν ενδιάμεσους καταλόγους στο `path` που δεν υπάρχουν. Το `os.removedirs(path)` θα αφαιρέσει τους ενδιάμεσους καταλόγους εφόσον είναι κενοί∙ εάν θέλετε να διαγράψετε ολόκληρο δέντρο καταλόγου και τα περιεχόμενα του, χρησιμοποιήστε τη `shutil.rmtree()`.

Για να μετονομάσετε ένα αρχείο, χρησιμοποιήστε το `os.rename(old_path, new_path)`.

Για να περικόψετε ένα αρχείο, ανοίξτε το χρησιμοποιώντας `f = open(filename, "rb+")`, και χρησιμοποιήστε το `f.truncate(offset)` ∙ το offset προεπιλέγεται στη τρέχουσα θέση αναζήτησης. Υπάρχει επίσης το `os.ftruncate(fd, offset)` για αρχεία που έχουν ανοιχτεί με `os.open()`, όπου fd είναι ο περιγραφέας αρχείου (ένας μικρός ακέραιος).

Το module `shutil` περιέχει επίσης έναν αριθμό λειτουργιών για εργασία σε αρχεία, όπως `copyfile()`, `copytree()`, και `rmtree()`.

### Πως αντιγράφω ένα αρχείο;

Το module `shutil` περιέχει μια συνάρτηση `copyfile()`. Σημειώστε ότι στους τόμους NTFS των Windows, δεν αντιγράφει alternate data streams ούτε resource forks σε τόμους macOS HFS+, αν και οι δύο χρησιμοποιούνται πλέον σπάνια. Επίσης δεν αντιγράφει δικαιώματα αρχείων και μεταδεδομένα, αν και η χρήση `shutil.copy2()` θα διατηρήσει το μεγαλύτερο μέρος (αν και όχι όλο) από αυτό.

### Πώς διαβάζω (ή γράφω) δυαδικά δεδομένα;

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

Για παράδειγμα, ο παρακάτω κώδικας διαβάζει δύο ακέραιους αριθμούς 2 byte και έναν ακέραιο αριθμό 4 byte σε μορφή big-endian από ένα αρχείο:

    import struct

    with open(filename, "rb") as f:
        s = f.read(8)
        x, y, z = struct.unpack(">hhl", s)

Το “>” στη συμβολοσειρά μορφής αναγκάζει δεδομένα big-endian∙ το γράμμα “h” διαβάζει έναν «short integer» (2 bytes), και το “l” διαβάζει έναν «long integer» (4 byte) από την συμβολοσειρά.

Για δεδομένα που είναι πιο κανονικά (π.χ. μια ομοιογενής λίστα ακεραίων ή αριθμούς με υποδιαστολή), μπορείτε επίσης να χρησιμοποιήσετε το module `array`.

Σημείωση

Για να διαβάσετε και να γράψετε δυαδικά δεδομένα, είναι υποχρεωτικό να ανοίξετε το αρχείο σε δυαδική λειτουργία (εδώ, περνώντας το `"rb"` στη `open()`). Εάν χρησιμοποιήσετε το `"r"` αντί αυτού (προεπιλεγμένο) το αρχείο θα είναι ανοιχτό σε λειτουργία κειμένου και το `f.read()` θα επιστρέψει αντικείμενα `str` αντί για αντικείμενα `bytes`.

### Δεν μπορώ να χρησιμοποιήσω το os.read() σε ένα pipe που δημιουργήθηκε με το os.popen()∙ γιατί;

Η `os.read()` είναι μια συνάρτηση χαμηλού επιπέδου που παίρνει έναν περιγραφέα αρχείου, έναν μικρό ακέραιο που αντιπροσωπεύει το ανοιχτό αρχείο. Η `os.popen()` δημιουργεί ένα αντικείμενο αρχείου υψηλού επιπέδου, του ίδιου τύπου που επιστρέφεται από την ενσωματωμένη συνάρτηση `open()`. Έτσι, για να διαβάσετε n byte από ένα pipe p που δημιουργήθηκε με τη `os.popen()`, πρέπει να χρησιμοποιήσετε το `p.read(n)`.

### Πώς μπορώ να αποκτήσω πρόσβαση στη σειριακή θύρα (RS232);

Για Win32, OSX, Linux, BSD, Jython, IronPython:

> pyserial

Για το Unix, δείτε μια ανάρτηση στο Usenet από τον Mitch Chapman:

> https://groups.google.com/groups?selm=34A04430.CF9@ohioee.com

### Γιατί το κλείσιμο του sys.stdout (stdin, stderr) δεν το κλείνει πραγματικά;

Το Python file objects είναι ένα επίπεδο αφαίρεσης υψηλού επιπέδου σε χαμηλού επιπέδου περιγραφείς αρχείων C.

Για τα περισσότερα αντικείμενα αρχείων που δημιουργείτε στην Python μέσω της ενσωματωμένης συνάρτησης `open()`, η `f.close()` επισημαίνει το αντικείμενο αρχείου Python ως κλειστό από την άποψη της Python και επίσης κανονίζει να κλείσει ο υποκείμενος περιγραφέας αρχείου C. Αυτό συμβαίνει επίσης αυτόματα στον καταστροφέα του `f`, όταν το `f` γίνεται σκουπίδια.

Αλλά τα stdin, stdout και stderr αντιμετωπίζονται ειδικά από την Python, λόγω της ειδικής κατάστασης που τους δίνεται επίσης από τη C. Εκτελώντας το `sys.stdout.close()` επισημαίνει το αντικείμενο αρχείου σε επίπεδο Python ως κλειστό, αλλά δεν κλείνει το συσχετισμένο περιγραφικό αρχείου C.

Για να κλείσετε τον υποκείμενο περιγραφέα αρχείου C για ένα από αυτά τα τρία, θα πρέπει πρώτα να βεβαιωθείτε ότι αυτό θέλετε πραγματικά να κάνετε (π.χ., μπορείτε να μπερδέψετε τα modules επέκτασης που προσπαθούν να κάνουν I/O). Εάν είναι, χρησιμοποιήστε τη `os.close()`:

    os.close(stdin.fileno())
    os.close(stdout.fileno())
    os.close(stderr.fileno())

Ή μπορείτε να χρησιμοποιήσετε τις αριθμητικές σταθερές 0, 1 και 2, αντίστοιχα.

## Προγραμματισμός Δικτύου/Διαδικτύου

### Ποια εργαλεία WWW υπάρχουν για την Python;

Δείτε τα κεφάλαια με τίτλο Πρωτόκολλα Internet και Υποστήριξη και Internet Data Handling στο Εγχειρίδιο Αναφοράς Βιβλιοθήκης. Η Python έχει πολλά modules που θα σας βοηθήσουν να δημιουργήσετε συστήματα ιστού από την πλευρά του διακομιστή και του πελάτη.

Μια σύνοψη των διαθέσιμων πλαισίων διατηρείται από τον Paul Boddie στη διεύθυνση https://wiki.python.org/moin/WebProgramming.

### Τι module πρέπει να χρησιμοποιήσω για να βοηθήσω στην δημιουργία HTML;

Μπορείτε να βρείτε μια συλλογή από χρήσιμους συνδέσμους στη σελίδα Web Programming wiki page.

### Πώς μπορώ να στείλω αλληλογραφία από ένα Python script;

Χρησιμοποιήστε την τυπική module βιβλιοθήκη `smtplib`.

Εδώ είναι ένας πολύ απλός διαδραστικός αποστολέας αλληλογραφίας που τον χρησιμοποιεί. Αυτή η μέθοδος θα λειτουργήσει σε οποιονδήποτε κεντρικό υπολογιστή υποστηρίζει το πρόγραμμα ακρόασης SMTP.

    import sys, smtplib

    fromaddr = input("From: ")
    toaddrs  = input("To: ").split(',')
    print("Enter message, end with ^D:")
    msg = ''
    while True:
        line = sys.stdin.readline()
        if not line:
            break
        msg += line

    # The actual mail send
    server = smtplib.SMTP('localhost')
    server.sendmail(fromaddr, toaddrs, msg)
    server.quit()

Μια εναλλακτική λύση μόνο για Unix χρησιμοποιεί sendmail. Η τοποθεσία του προγράμματος sendmail ποικίλλει μεταξύ των συστημάτων∙ μερικές φορές είναι `/usr/lib/sendmail`, μερικές φορές `/usr/sbin/sendmail`. Η σελίδα εγχειριδίου sendmail θα σας βοηθήσει. Ορίστε ένα δείγμα κώδικα:

    import os

    SENDMAIL = "/usr/sbin/sendmail"  # sendmail location
    p = os.popen("%s -t -i" % SENDMAIL, "w")
    p.write("To: receiver@example.com\n")
    p.write("Subject: test\n")
    p.write("\n")  # blank line separating headers from body
    p.write("Some text\n")
    p.write("some more text\n")
    sts = p.close()
    if sts != 0:
        print("Sendmail exit status", sts)

### Πώς μπορώ να αποφύγω τον αποκλεισμό στη μέθοδο connect() ενός socket;

Το module `select` χρησιμοποιείται συνήθως για να βοηθήσει με ασύγχρονες I/O σε sockets.

Για να αποτρέψετε τον αποκλεισμό της σύνδεσης TCP, μπορείτε να ρυθμίσετε την υποδοχή σε λειτουργία μη αποκλεισμού. Στην συνέχεια, όταν κάνετε τη `connect()`, είτε θα συνδεθείτε αμέσως (απίθανο), είτε θα λάβετε μια εξαίρεση που περιέχει τον αριθμό σφάλματος ως `.errno`. Το `errno.EINPROGRESS` υποδηλώνει ότι η σύνδεση είναι σε εξέλιξη, αλλά δεν έχει ολοκληρωθεί ακόμα. Διαφορετικά λειτουργικά συστήματα θα επιστρέψουν διαφορετικές τιμές, επομένως θα πρέπει να ελέγξετε τι επιστρέφεται στο σύστημα σας.

Μπορείτε να χρησιμοποιήσετε τη μέθοδο `connect_ex()` για να αποφύγετε τη δημιουργία εξαίρεσης. Απλώς θα επιστρέψει την τιμή errno. Για δημοσκόπηση, μπορείτε να καλέσετε το `connect_ex()` ξανά αργότερα – `0` ή `errno.EISCONN` υποδηλώνει ότι είστε συνδεδεμένοι – ή μπορείτε να περάσετε αυτό το socket στη `select.select()` για να ελέγξτε αν είναι εγγράψιμο.

Σημείωση

Το module `asyncio` παρέχει μια γενικού σκοπού απλή και ταυτόχρονη ασύγχρονη βιβλιοθήκη, η οποία μπορεί να χρησιμοποιηθεί για τη σύνταξη κώδικα δικτύου χωρίς αποκλεισμό. Η βιβλιοθήκη Twisted είναι μια δημοφιλής και με πολλές λειτουργίες εναλλακτική λύση.

## Βάσεις Δεδομένων

### Υπάρχουν διεπαφές σε πακέτα βάσεων δεδομένων στην Python;

Ναι.

Διεπαφές σε κατακερματισμούς που βασίζονται σε δίσκο, όπως `DBM` και `GDBM` περιλαμβάνονται επίσης στην τυπική Python. Υπάρχει επίσης το module `sqlite3`, το οποίο παρέχει μια ελαφριά σχεσιακή βάση δεδομένων που βασίζεται στο δίσκο.

Υποστήριξη για τις περισσότερες σχεσιακές βάσεις δεδομένων είναι διαθέσιμη. Δείτε τη σελίδα DatabaseProgramming wiki page για λεπτομέρειες.

### Πως υλοποιείτε persistent αντικείμενα στην Python;

To module βιβλιοθήκης `pickle` το λύνει αυτό με πολύ γενικό τρόπο (αν και δεν μπορείτε ακόμα να αποθηκεύσετε πράγματα όπως ανοιχτά αρχεία, sockets ή παράθυρα), και το module βιβλιοθήκης `shelve` χρησιμοποιεί pickle και (g)dbm για τη δημιουργία επίμονων αντιστοιχίσεων που περιέχουν αυθαίρετα αντικείμενα Python.

## Μαθηματικά και Αριθμητικά

### Πως μπορώ να δημιουργήσω τυχαίους αριθμούς στην Python;

Το τυπικό module `random` υλοποιεί μια γεννήτρια τυχαίων αριθμών. Η χρήση είναι απλή:

    import random
    random.random()

Αυτό επιστρέφει έναν τυχαίο αριθμό κινητής υποδιαστολής στο εύρος [0, 1).

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

- Το `randrange(a, b)` επιλέγει έναν ακέραιο στο εύρος [a, b).

- Το `uniform(a, b)` επιλέγει έναν αριθμό κινητής υποδιαστολής στο εύρος [a, b).

- Το `normalvariate(mean, sdev)` λαμβάνει δείγματα της κανονικής (Gaussian) κατανομής.

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

- To `choice(S)` επιλέγει ένα τυχαίο στοιχείο από μια δεδομένη ακολουθία.

- Το `shuffle(L)` ανακατεύει μια λίστα επιτόπου, δηλαδή τη μεταθέτει τυχαία.

Υπάρχει επίσης μια κλάση `Random` που μπορείτε να επιβεβαιώσετε για να δημιουργήσετε ανεξάρτητες γεννήτριες πολλαπλών τυχαίων αριθμών.