11. Σύντομη περιήγηση στην Πρότυπη Βιβλιοθήκη — Μέρος II¶
Αυτή η δεύτερη περιήγηση καλύπτει τα πιο προηγμένα modules που υποστηρίζουν επαγγελματικές ανάγκες προγραμματισμού. Αυτά τα modules σπάνια εμφανίζονται σε μικρά scripts.
11.1. Μορφοποίηση εξόδου¶
Το module reprlib
παρέχει μια έκδοση του repr()
προσαρμοσμένη για συντομευμένες εμφανίσεις μεγάλων ή βαθιά ένθετων containers:
>>> import reprlib
>>> reprlib.repr(set('supercalifragilisticexpialidocious'))
"{'a', 'c', 'd', 'e', 'f', 'g', ...}"
Το module pprint
προσφέρει πιο εξελιγμένο έλεγχο της εκτύπωσης τόσο των ενσωματωμένων (built-in) και των καθορισμένων από τον χρήστη αντικειμένων με τρόπο που είναι ευανάγνωστο από τον διερμηνέα. Όταν το αποτέλεσμα είναι μεγαλύτερο από μία γραμμή, ο «pretty printer» προσθέτει αλλαγές γραμμής και εσοχές για να εμφανιστεί πιο ξεκάθαρα η δομή δεδομένων:
>>> import pprint
>>> t = [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta',
... 'yellow'], 'blue']]]
...
>>> pprint.pprint(t, width=30)
[[[['black', 'cyan'],
'white',
['green', 'red']],
[['magenta', 'yellow'],
'blue']]]
Το module textwrap
μορφοποιεί τις παραγράφους του κειμένου ώστε να ταιριάζει σε ένα δεδομένο πλάτος οθόνης:
>>> import textwrap
>>> doc = """The wrap() method is just like fill() except that it returns
... a list of strings instead of one big string with newlines to separate
... the wrapped lines."""
...
>>> print(textwrap.fill(doc, width=40))
The wrap() method is just like fill()
except that it returns a list of strings
instead of one big string with newlines
to separate the wrapped lines.
Το module locale
έχει πρόσβαση σε μια βάση δεδομένων μορφών δεδομένων συγκεκριμένης κουλτούρας. Το χαρακτηριστικό ομαδοποίησης της συνάρτησης μορφοποίησης της τοπικής ρύθμισης παρέχει έναν άμεσο τρόπο μορφοποίησης αριθμών με διαχωριστικά ομάδων:
>>> import locale
>>> locale.setlocale(locale.LC_ALL, 'English_United States.1252')
'English_United States.1252'
>>> conv = locale.localeconv() # get a mapping of conventions
>>> x = 1234567.8
>>> locale.format("%d", x, grouping=True)
'1,234,567'
>>> locale.format_string("%s%.*f", (conv['currency_symbol'],
... conv['frac_digits'], x), grouping=True)
'$1,234,567.80'
11.2. Templating¶
Το module string
περιλαμβάνει μια ευέλικτη κλάση Template
με απλοποιημένη σύνταξη κατάλληλη για επεξεργασία από τελικούς χρήστες. Αυτό επιτρέπει στους χρήστες να προσαρμόζουν τις εφαρμογές τους χωρίς να χρειάζεται να αλλάξουν την εφαρμογή.
Η μορφή χρησιμοποιεί ονόματα κράτησης θέσης που σχηματίζονται από $
με έγκυρα αναγνωριστικά Python (αλφαριθμητικούς χαρακτήρες και κάτω παύλες). Περιβάλλοντας το placeholder με αγκύλες επιτρέπει να ακολουθείται από περισσότερα αλφαριθμητικά γράμματα χωρίς ενδιάμεσα κενά. Γράφοντας $$
δημιουργεί ένα ενιαίο $
:
>>> from string import Template
>>> t = Template('${village}folk send $$10 to $cause.')
>>> t.substitute(village='Nottingham', cause='the ditch fund')
'Nottinghamfolk send $10 to the ditch fund.'
Η μέθοδος substitute()
κάνει raise ένα KeyError
όταν ένα placeholder δεν παρέχεται σε ένα λεξικό ή ένα όρισμα λέξης-κλειδιού. Για εφαρμογές στυλ συγχώνευσης mail, τα δεδομένα που παρέχονται από τον χρήστη ενδέχεται να είναι ελλιπή και η μέθοδος safe_substitute()
μπορεί να είναι πιο κατάλληλη — θα αφήσει αμετάβλητα τα placeholders εάν λείπουν δεδομένα:
>>> t = Template('Return the $item to $owner.')
>>> d = dict(item='unladen swallow')
>>> t.substitute(d)
Traceback (most recent call last):
...
KeyError: 'owner'
>>> t.safe_substitute(d)
'Return the unladen swallow to $owner.'
Οι υποκατηγορίες προτύπων μπορούν να καθορίσουν έναν προσαρμοσμένο οριοθέτη. Για παράδειγμα, ένα batch πρόγραμμα μετονομασίας για ένα πρόγραμμα περιήγησης φωτογραφιών μπορεί να επιλέξει να χρησιμοποιεί σύμβολα ποσοστού για placeholders όπως η τρέχουσα ημερομηνία, ο αριθμός ακολουθίας εικόνων ή η μορφή αρχείου:
>>> import time, os.path
>>> photofiles = ['img_1074.jpg', 'img_1076.jpg', 'img_1077.jpg']
>>> class BatchRename(Template):
... delimiter = '%'
>>> fmt = input('Enter rename style (%d-date %n-seqnum %f-format): ')
Enter rename style (%d-date %n-seqnum %f-format): Ashley_%n%f
>>> t = BatchRename(fmt)
>>> date = time.strftime('%d%b%y')
>>> for i, filename in enumerate(photofiles):
... base, ext = os.path.splitext(filename)
... newname = t.substitute(d=date, n=i, f=ext)
... print('{0} --> {1}'.format(filename, newname))
img_1074.jpg --> Ashley_0.jpg
img_1076.jpg --> Ashley_1.jpg
img_1077.jpg --> Ashley_2.jpg
Μια άλλη εφαρμογή για templating είναι ο διαχωρισμός της λογικής του προγράμματος από τις λεπτομέρειες πολλαπλών μορφών εξόδου. Αυτό καθιστά δυνατή την αντικατάσταση προσαρμοσμένων προτύπων για αρχεία XML, αναφορές απλού κειμένου και αναφορές ιστού HTML.
11.3. Εργασία με δυαδικές διατάξεις εγγραφής δεδομένων¶
Το module struct
παρέχει τις συναρτήσεις pack()
και unpack()
για εργασία με μορφές δυαδικών (binary) εγγραφών μεταβλητού μήκους. Το ακόλουθο παράδειγμα δείχνει πως να κάνετε μια λούπα μέσω των πληροφοριών κεφαλίδας στο ένα αρχείο ZIP χωρίς τη χρήση του module zipfile
. Οι κωδικοί πακέτου "H"
και "I"
αντιπροσωπεύουν αριθμούς χωρίς υπογραφή δύο και τεσσάρων byte αντίστοιχα. Το "<"
υποδηλώνει ότι είναι τυπικού μεγέθους και σε σειρά byte λίγο endian:
import struct
with open('myfile.zip', 'rb') as f:
data = f.read()
start = 0
for i in range(3): # show the first 3 file headers
start += 14
fields = struct.unpack('<IIIHH', data[start:start+16])
crc32, comp_size, uncomp_size, filenamesize, extra_size = fields
start += 16
filename = data[start:start+filenamesize]
start += filenamesize
extra = data[start:start+extra_size]
print(filename, hex(crc32), comp_size, uncomp_size)
start += extra_size + comp_size # skip to the next header
11.4. Multi-threading¶
Το threading είναι μια τεχνική για την αποσύνδεση εργασιών που είναι διαδοχικά εξαρτώμενες. Τα νήματα μπορούν να χρησιμοποιηθούν για την βελτίωση της ανταπόκρισης των εφαρμογών που δέχονται είσοδο από τον χρήστη ενώ άλλες εργασίες εκτελούνται στο παρασκήνιο. Μια σχετική περίπτωση χρήσης εκτελεί I/O παράλληλα με υπολογισμούς στο άλλο νήμα.
Ο ακόλουθος κώδικας δείχνει πως το module υψηλού επιπέδου threading
μπορεί να εκτελεί εργασίες στο παρασκήνιο ενώ το κύριο πρόγραμμα συνεχίζει να εκτελείται:
import threading, zipfile
class AsyncZip(threading.Thread):
def __init__(self, infile, outfile):
threading.Thread.__init__(self)
self.infile = infile
self.outfile = outfile
def run(self):
f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
f.write(self.infile)
f.close()
print('Finished background zip of:', self.infile)
background = AsyncZip('mydata.txt', 'myarchive.zip')
background.start()
print('The main program continues to run in foreground.')
background.join() # Wait for the background task to finish
print('Main program waited until background was done.')
Η κύρια πρόκληση των εφαρμογών πολλαπλών νημάτων είναι ο συντονισμός νημάτων που μοιράζονται δεδομένα ή άλλους πόρους. Για το σκοπό αυτό, το thread module παρέχει έναν αριθμό πρωτόγονων συγχρονισμού, συμπεριλαμβανομένων locks, events, μεταβλητών συνθηκών, και semaphores.
Ενώ αυτά τα εργαλεία είναι ισχυρά, μικρά σφάλματα σχεδιασμού μπορεί να οδηγήσουν σε προβλήματα που είναι δύσκολο να αναπαραχθούν. Επομένως, η προτιμώμενη προσέγγιση στον συντονισμό εργασιών είναι να συγκεντρωθεί όλη η πρόσβαση σε έναν πόρο σε ένα μόνο νήμα και στη συνέχεια να χρησιμοποιηθεί το module queue
, για τροφοδοτήσει αυτό το νήμα με αιτήματα από άλλα νήματα. Οι εφαρμογές που χρησιμοποιούν αντικείμενα Queue
για επικοινωνία και συντονισμό μεταξύ νημάτων είναι πιο εύκολο να σχεδιαστούν, είναι πιο ευανάγνωστες και πιο αξιόπιστες.
11.5. Logging¶
Το module logging
προσφέρει ένα πλήρως εξοπλισμένο και ευέλικτο σύστημα καταγραφής. Στην απλούστερη μορφή του, τα μηνύματα καταγραφής αποστέλλονται σε ένα αρχείο ή στο sys.stderr
:
import logging
logging.debug('Debugging information')
logging.info('Informational message')
logging.warning('Warning:config file %s not found', 'server.conf')
logging.error('Error occurred')
logging.critical('Critical error -- shutting down')
Αυτό παράγει την ακόλουθη έξοδο:
WARNING:root:Warning:config file server.conf not found
ERROR:root:Error occurred
CRITICAL:root:Critical error -- shutting down
Από default (προεπιλογή), τα ενημερωτικά μηνύματα και τα μηνύματα εντοπισμού σφαλμάτων αποκρύπτονται και η έξοδος αποστέλλεται σε τυπικό σφάλμα. Άλλες επιλογές εξόδου περιλαμβάνουν δρομολόγηση μηνυμάτων μέσω email, datagrams, υποδοχών (sockets), ή σε HTTP διακομιστή (server). Τα νέα φίλτρα μπορούν να επιλέξουν διαφορετική δρομολόγηση με βάση την προτεραιότητα του μηνύματος: DEBUG
, INFO
, WARNING
, ERROR
, και CRITICAL
.
Το σύστημα logging μπορεί να διαμορφωθεί απευθείας από την Python ή μπορεί να φορτωθεί από ένα επεξεργάσιμο αρχείο διαμόρφωσης για προσαρμοσμένη καταγραφή χωρίς την τροποποίηση της εφαρμογής.
11.6. Αδύναμες αναφορές¶
Η Python κάνει αυτόματη διαχείριση μνήμης (καταμέτρηση αναφορών για τα περισσότερα αντικείμενα και garbage collection για την εξάλειψη των κύκλων). Η μνήμη ελευθερώνεται λίγο μετά την κατάργηση της τελευταίας αναφοράς σε αυτήν.
Αυτή η προσέγγιση λειτουργεί καλά για τις περισσότερες εφαρμογές, αλλά περιστασιακά υπάρχει ανάγκη παρακολούθησης αντικειμένων μόνο εφόσον χρησιμοποιούνται από κάτι άλλο. Δυστυχώς, η παρακολούθηση τους δημιουργεί μια αναφορά που τα κάνει μόνιμα. Το module weakref
παρέχει εργαλεία για την παρακολούθηση αντικειμένων χωρίς τη δημιουργία αναφοράς. Όταν το αντικείμενο δεν χρειάζεται πλέον, αφαιρείται αυτόματα από έναν πίνακα ασθενούς αναφοράς και ενεργοποιείται μια επιστροφή κλήσης για αντικείμενα ασθενούς αναφοράς:
>>> import weakref, gc
>>> class A:
... def __init__(self, value):
... self.value = value
... def __repr__(self):
... return str(self.value)
...
>>> a = A(10) # create a reference
>>> d = weakref.WeakValueDictionary()
>>> d['primary'] = a # does not create a reference
>>> d['primary'] # fetch the object if it is still alive
10
>>> del a # remove the one reference
>>> gc.collect() # run garbage collection right away
0
>>> d['primary'] # entry was automatically removed
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
d['primary'] # entry was automatically removed
File "C:/python39/lib/weakref.py", line 46, in __getitem__
o = self.data[key]()
KeyError: 'primary'
11.7. Εργαλεία για εργασία με λίστες¶
Πολλές ανάγκες δομών δεδομένων μπορούν να καλυφθούν με τον ενσωματωμένο τύπο λίστας. Ωστόσο, μερικές φορές υπάρχει ανάγκη για εναλλακτικές υλοποιήσεις με διαφορετικούς συμβιβασμούς απόδοσης.
The array
module provides an array()
object that is like
a list that stores only homogeneous data and stores it more compactly. The
following example shows an array of numbers stored as two byte unsigned binary
numbers (typecode "H"
) rather than the usual 16 bytes per entry for regular
lists of Python int objects:
>>> from array import array
>>> a = array('H', [4000, 10, 700, 22222])
>>> sum(a)
26932
>>> a[1:3]
array('H', [10, 700])
The collections
module provides a deque()
object
that is like a list with faster appends and pops from the left side but slower
lookups in the middle. These objects are well suited for implementing queues
and breadth first tree searches:
>>> from collections import deque
>>> d = deque(["task1", "task2", "task3"])
>>> d.append("task4")
>>> print("Handling", d.popleft())
Handling task1
unsearched = deque([starting_node])
def breadth_first_search(unsearched):
node = unsearched.popleft()
for m in gen_moves(node):
if is_goal(m):
return m
unsearched.append(m)
Εκτός από τις εναλλακτικές υλοποιήσεις λιστών, η βιβλιοθήκη προσφέρει επίσης και άλλα εργαλεία όπως το module bisect
με συναρτήσεις για τον χειρισμό ταξινομημένων λιστών:
>>> import bisect
>>> scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500, 'python')]
>>> bisect.insort(scores, (300, 'ruby'))
>>> scores
[(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500, 'python')]
Το module heapq
παρέχει λειτουργίες για την υλοποίηση σωρών (heaps) που βασίζονται σε κανονικές λίστες. Η καταχώριση με την χαμηλότερη τιμή διατηρείται πάντα στη θέση μηδέν. Αυτό είναι χρήσιμο για εφαρμογές που έχουν επανειλημμένα πρόσβαση στο μικρότερο στοιχείο αλλά δεν θέλουν να εκτελέσουν μια πλήρη ταξινόμηση λίστας:
>>> from heapq import heapify, heappop, heappush
>>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
>>> heapify(data) # rearrange the list into heap order
>>> heappush(data, -5) # add a new entry
>>> [heappop(data) for i in range(3)] # fetch the three smallest entries
[-5, 0, 1]
11.8. Decimal Floating Point Arithmetic¶
The decimal
module offers a Decimal
datatype for
decimal floating point arithmetic. Compared to the built-in float
implementation of binary floating point, the class is especially helpful for
οικονομικές εφαρμογές και άλλες χρήσεις που απαιτούν ακριβή δεκαδική αναπαράσταση,
έλεγχος για την ακρίβεια,
έλεγχος της στρογγυλοποίησης για την εκπλήρωση νομικών ή κανονιστικών απαιτήσεων,
παρακολούθηση σημαντικών δεκαδικών ψηφίων, ή
εφαρμογές όπου ο χρήστης αναμένει ότι τα αποτελέσματα ταιριάζουν με υπολογισμούς που έγιναν με το χέρι.
Για παράδειγμα, ο υπολογισμός ενός φόρου 5% σε χρέωση τηλεφώνου 70 λεπτών δίνει διαφορετικά αποτελέσματα σε δεκαδική κινητή υποδιαστολή και σε δυαδική (binary) κινητή υποδιαστολή. Η διαφορά γίνεται σημαντική εάν τα αποτελέσματα στρογγυλοποιηθούν στο πλησιέστερο λεπτό:
>>> from decimal import *
>>> round(Decimal('0.70') * Decimal('1.05'), 2)
Decimal('0.74')
>>> round(.70 * 1.05, 2)
0.73
Το αποτέλεσμα Decimal
διατηρεί ένα μηδέν στο τέλος, συνάγοντας αυτόματα τη σημασία τεσσάρων θέσεων από πολλαπλάσια με σημασία δύο θέσεων. Το δεκαδικό αναπαράγει τα μαθηματικά όπως γίνονται με το χέρι και αποφεύγει ζητήματα που μπορεί να προκύψουν όταν η δυαδική κινητή υποδιαστολή δεν μπορεί να αντιπροσωπεύει ακριβώς τις δεκαδικές ποσότητες.
Η ακριβής αναπαράσταση επιτρέπει στην κλάση Decimal
να εκτελεί υπολογισμούς modulo και δοκιμές ισότητας που είναι ακατάλληλες για δυαδική κινητή υποδιαστολή:
>>> Decimal('1.00') % Decimal('.10')
Decimal('0.00')
>>> 1.00 % 0.10
0.09999999999999995
>>> sum([Decimal('0.1')]*10) == Decimal('1.0')
True
>>> sum([0.1]*10) == 1.0
False
Το module decimal
παρέχει αριθμητική με όση ακρίβεια χρειάζεται:
>>> getcontext().prec = 36
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857')