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


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


Πως μπορώ να βρω ένα 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__ = """...Whatever..."""


Υπάρχει ένα 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;
---------------------------------------------------------

The "atexit" module provides a register function that is similar to
C's "onexit()".


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

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

   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

Operations that replace other objects may invoke those other objects'
"__del__()" method when their reference count reaches zero, and that
can affect things.  This is especially true for the mass updates to
dictionaries and lists.  When in doubt, use a mutex!


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

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

Πίσω στις μέρες της Python 1.5, ο Greg Stein υλοποίησε πραγματικά ένα
ολοκληρωμένο σύνολο επιδιορθώσεων (οι επιδιορθώσεις "ελεύθερου
νήματος") που αφαίρεσε το GIL και το αντικατέστησε με ένα άριστα
προσεγμένο κλειδωμένο thread.  Ο Adam Olsen έκανε πρόσφατα ένα
παρόμοιο πείραμα στο έργο python-safethread.  Δυστυχώς, και τα δύο
πειράματα εμφάνισαν απότομη πτώση στην απόδοση ενός νήματος
(τουλάχιστον 30% πιο αργή), λόγω της ποσότητας του προσεγμένου
κλειδώματος που απαιτείται για την αντιστάθμιση της αφαίρεσης του GIL.

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

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

Έχει προταθεί ότι το GIL θα πρέπει να είναι ένα κλείδωμα ανά κατάσταση
διερμηνέα αντί να είναι πραγματικά καθολικό∙ τότε οι διερμηνείς δεν θα
μπορούν να μοιράζονται αντικείμενα. Δυστυχώς, ούτε αυτό είναι πιθανό
να συμβεί.  Θα ήταν τεράστιος όγκος δουλειάς, επειδή πολλές
υλοποιήσεις αντικειμένων επί του παρόντος έχουν παγκόσμια κατάσταση.
Για παράδειγμα, μικροί ακέραιοι αριθμοί και σύντομες συμβολοσειρές
αποθηκεύονται στην κρυφή μνήμη∙ αυτές οι κρυφές μνήμες θα πρέπει να
μετακινηθούν στην κατάσταση διερμηνέα. Άλλοι τύποι αντικειμένων έχουν
τη δική τους κενή λίστα∙ αυτές οι κενές λίστες θα πρέπει να
μετακινηθούν στην κατάσταση διερμηνέα. Και ούτω καθεξής.

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

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


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


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

Χρησιμοποιήστε τα "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)" ∙
μετατόπιση των προεπιλογών στην τρέχουσα θέση αναζήτησης.  Υπάρχει
επίσης το "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:

   https://pypi.org/project/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.

Η Cameron Laird διατηρεί ένα χρήσιμο σύνολο σελίδων σχετικά με τις
τεχνολογίες Ιστού Python στη διεύθυνση https://web.archive.org/web/20
210224183619/http://phaseit.net/claird/comp.lang.python/web_python.


Πώς μπορώ να μιμηθώ την υποβολή φόρμας CGI (METHOD=POST);
---------------------------------------------------------

Θα ήθελα να ανακτήσω ιστοσελίδες που είναι αποτέλεσμα του POST μιας
φόρμας.  Υπάρχει κώδικας που θα επέτρεπε να το κάνω εύκολα;

Ναι. Ακολουθεί ένα απλό παράδειγμα που χρησιμοποιεί "urllib.request":

   #!/usr/local/bin/python

   import urllib.request

   # build the query string
   qs = "First=Josephine&MI=Q&Last=Public"

   # connect and send the server a path
   req = urllib.request.urlopen('http://www.some-server.out-there'
                                '/cgi-bin/some-cgi-script', data=qs)
   with req:
       msg, hdrs = req.read(), req.info()

Λάβετε υπόψιν ότι γενικά για λειτουργίες POST με κωδικοποίηση
ποσοστού, οι συμβολοσειρές ερωτημάτων πρέπει να αναφέρονται
χρησιμοποιώντας τη "urllib.parse.urlencode()".  Για παράδειγμα, για να
στείλετε "name=Guy Steele, Jr.":

   >>> import urllib.parse
   >>> urllib.parse.urlencode({'name': 'Guy Steele, Jr.'})
   'name=Guy+Steele%2C+Jr.'

Δείτε επίσης:

  HOWTO Fetch Internet Resources Using The urllib Package για εκτενή
  παραδείγματα.


Τι 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.

To prevent the TCP connect from blocking, you can set the socket to
non-blocking mode.  Then when you do the "socket.connect()", you will
either connect immediately (unlikely) or get an exception that
contains the error number as ".errno". "errno.EINPROGRESS" indicates
that the connection is in progress, but hasn't finished yet.
Different OSes will return different values, so you're going to have
to check what's returned on your system.

You can use the "socket.connect_ex()" method to avoid creating an
exception.  It will just return the errno value.  To poll, you can
call "socket.connect_ex()" again later -- "0" or "errno.EISCONN"
indicate that you're connected -- or you can pass this socket to
"select.select()" to check if it's writable.

Σημείωση:

  The "asyncio" module provides a general purpose single-threaded and
  concurrent asynchronous library, which can be used for writing non-
  blocking network code. The third-party Twisted library is a popular
  and feature-rich alternative.


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


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

Ναι.

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

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


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

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


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


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

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

   import random
   random.random()

This returns a random floating point number in the range [0, 1).

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

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

* "uniform(a, b)" chooses a floating point number in the range [a, b).

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

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

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

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

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