Python3.4 Tutorial11 - Brief Tour of the Standard Library – Part II

walkerMK發表於2015-09-26

11 標準庫簡介 - 第二部分

    第二部分的瀏覽包含更多的高階模組,可以支援專業的程式設計需要。這些模組很少出現在小段的指令碼中。

11.1 輸出格式化

    reprlib模組提供repr()的一個定製版本,它專門用於縮排顯示大型或深層巢狀的容器:

>>> import reprlib
>>> reprlib.repr(set('supercalifragilisticexpialidocious'))
"set(['a', 'c', 'd', 'e', 'f', 'g', ...])"

    pprint模組提供了更復雜的控制系統,在直譯器可以讀到(定義內容)的情況下,它可以列印出內建物件和使用者自定義物件的定義文字。當結果超過一行的時候,“列印美化”功能會新增換行符和縮排使資料的結構更加清晰可見:

>>> import pprint
>>> t = [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta',
...     'yellow'], 'blue']]]
...
>>> pprint.pprint(t, width=30)
[[[['black', 'cyan'],
   'white',
   ['green', 'red']],
  [['magenta', 'yellow'],
   'blue']]]

    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.

    locale模組可以訪問記錄了不同文化下資料格式差異的資料庫。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)

    string模組包含一個多功能的Template類,它簡化的語法適合終端使用者的編輯需要。這允許使用者定製他們的應用而不要修改應用內容。

    格式化操作使用$加合法的Python標誌符(包括字母、數字和下劃線)作為佔位符。在佔位符周圍加上花括號,可以在後面跟著其他字母或數字而不用插入空格。可以使用$$來轉義$

>>> 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()方法會引發一個KeyError。在mail-merge風格的應用中,使用者提供的資料可能不全,這是使用safe_substitute()方法會更合適——當沒有提供資料時,會原樣顯示佔位符:

>>> 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.'

    Template的子類可以自己指定一個分隔符。例如,一個圖片瀏覽器的批量重新命名工具,可能選擇百分號作為佔位符用於表示當前日期、圖片序列號或檔案格式等:

>>> 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

    模板化的另一個應用是,將程式邏輯從多樣化輸出格式化的細節中分離出來。這使得使用自定義模板替代XML檔案、純文字報表和HTML網頁報表成為可能。

11.3 使用二進位制資料記錄佈局

    struct模組提供了pack()unpack()函式,用來處理不定長二進位制記錄的格式化。下面的例子展示瞭如何不使用zipfile模組遍歷Zip檔案頭資訊。打包程式碼中"H""I"分別代表2byte和4byte的無符號數字。"<"符號代表它們使用標準大大小並且使用小端位元組序:

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 多執行緒

    執行緒是一種將不依賴執行順序的任務解耦的技術。其他任務在後臺執行時,執行緒可以用來改善應用程式對使用者輸入的響應性。一個相關的用例是,可以是I/O和另一個執行緒中的計算併發執行。

    下面的程式碼展示了高層的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.')

    多執行緒應用程式最重要的挑戰是,協調執行緒間的共享資料或其他資源。為此,threading模組提供了許多原語,包括鎖(locks)、事件(events)、條件變數(condition variables)、訊號量(semaphores)。

    雖然那些工具很強大,但是微小的設計錯誤可能導致難以復現的問題。所以,協調任務的首選方案是將所有資源訪問都集中在一個執行緒中,然後使用queue模組來處理來自其他執行緒的請求。應用程式使用Queue物件處理執行緒間的通訊和協調,可以使設計更簡單,可讀性、可靠性更高。

11.5 日誌

    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

    預設情況下,資訊和除錯訊息會被抑制,並且它們是被髮送到標準錯誤流中輸出。其他的輸出選項包括經由郵件、資料包、套接字的路由訊息或者是傳送給一個HTTP伺服器。新的過濾器可以根據訊息的優先順序選擇不同的路由,優先順序有:DEBUG、INFO、WARNING,、ERROR和CRITICAL。

    日誌系統可以在不改變應用程式的情況下,直接通過Python進行配置,也可以載入一個使用者可編輯的配置檔案來定製日誌內容。

11.6 弱引用(Weak References)

    Python會自動進行記憶體管理(為大多數物件進行引用計數,週期性進行垃圾回收)。在最後一個引用消失後不久記憶體就會被釋放。

    這種機制適用於大多數應用程式, 但也有偶然情況, 只有當它們被其它東西使用時才需要去跟蹤物件。不幸的是,僅是要追蹤它們也需要建立的一個引用,這會使它們永久存在(翻譯的可能有誤,原文:just tracking them creates a reference that makes them permanent)。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:/python34/lib/weakref.py", line 46, in __getitem__
    o = self.data[key]()
KeyError: 'primary'

11.7 使用Lists的工具

    對很多資料結構的需求都可使用內建的list型別來滿足。然而,有時候需要在不同的效能權衡中選擇一種實現方式。

    array模組提供了一個array()物件,就像一個list,可以儲存同一型別的資料並且更緊湊(這裡應該是指記憶體開銷小)。下面這個例子演示了將一組數字以2byte的無符號二進位制數(型別碼 "H") 形式儲存為一個array,而不是常規的list儲存的每項16byte的Python整數物件(原文: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])

    collections模組提供了一個deque()物件,和list類似,但是它在左側的append、pop操作速度快,在中間的查詢很慢。這些物件適合實現佇列和廣度優先樹的查詢:

>>> 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)

    除了選擇不同的連結串列實現之外,這個庫還提供了其他工具,例如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')]

    heapq模組提供了一些基於常規list實現堆的函式。輸入的最小的值總是保持在位置0處。這對於重複訪問最小的元素但又不想對整個列表進行排序的應用程式來說十分有用:

>>> 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模組提供了一個Decimal資料型別,用於十進位制浮點運算。相比於使用二進位制浮點實現的內建float型別,這個類在下列情況中特別有用:

  • 金融應用和其他需要精確十進位制表示的地方
  • 需要精度控制
  • 需要控制四捨五入,以滿足法律或監管的要求
  • 需要跟蹤有意義的小數部分
  • 使用者期望應用的結果和手動計算的結果相匹配

    例如,在計算70美分話費的5%的稅收時,十進位制浮點數和二進位制浮點數的結果是不同的。如果結果以美分的精讀進行舍入的話,這個差別就會變得很重要:

>>> from decimal import *
>>> round(Decimal('0.70') * Decimal('1.05'), 2)
Decimal('0.74')
>>> round(.70 * 1.05, 2)
0.73

    Decimal結果會保持尾部的0,並會從兩位有效數字的數乘中自動推匯出思維有效數字。Decimal再現手工數學運算的精讀,避免二進位制浮點小數點不能準確地表示十進位制數時出現的問題。

    高精度使的Decimal類可以去做那些二進位制浮點數不適合的模運算和相等測試:

>>> 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

   decimal可以滿足你所需要的算術精讀:

>>> getcontext().prec = 36
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857')

相關文章