第二部分涵盖了更高阶的模块,支持更专业的编程需求,这些模块很少出现在小的脚本中。
格式化输出
reprlib
提供一系列repr()
对很大或深度层叠的容器进行定制化的压缩展示。
import reprlib reprlib.repr(set('supercalifragilisticexpialidocious'))
pprint
提供更成熟的控制,将内建对象和用户定义对象更易读地打印出来,当结果长于一行时,”pretty printer”添加行数和缩进来将数据结构更清楚地展现出来。
import pprint t = [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta', 'yellow'], 'blue']]] pprint.pprint(t, width=30)
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))
local
为各种数据格式的数据库提供统一的访问入口,local的格式化功能的group属性提供了一种用组分隔符直接分隔数字的方式。
import locale locale.setlocale(locale.LC_ALL, 'English_United States.1252') conv = locale.localeconv() # get a mapping of conventions x = 1234567.8 locale.format("%d", x, grouping=True) locale.format_string("%s%.*f", (conv['currency_symbol'], conv['frac_digits'], x), grouping=True
模板
string
包含了一个多功能的 Template
类,通过它用户可以通过合适的语法进行编辑操作,它允许用户在不对应用做修改的方式进行定制化调整。
模板使用$
和Python有效标识符(数字,字母,下划线)组合形式的占位符名称,带有{}的占位符后可以追加没有空格的字符数字和字母,在文本中使用单个$需要在模板中输入$$
from string import Template t = Template('${village}folk send $$10 to $cause.') t.substitute(village='Nottingham', cause='the ditch fund')
当字典或关键参数没有提供占位符对应的数据时,substitute()
会产生一个KeyError
,对mail-merge风格的应用,用户提供的数据可能是不完整的,这时safe_substitute()
是更合适的-如果数据缺失,它将保持占位符不变
t = Template('Return the $item to $owner.') d = dict(item='unladen swallow') t.substitute(d) t.safe_substitute(d)
模板子类可以指定自定义分隔符。例如,照片浏览器的批量重命名程序可以选择使用百分比符号做占位符,例如当前日期、图像序列号或文件格式:
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): ') 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))
模板的另一个实用功能是将逻辑处理和复杂的输出细节分离开来,可以将模板替换为XML文件、纯文本报告和HTML Web报表等多种形式
二进制数据记录设计
struct
提供 pack()
和 unpack()
,用于处理可变长度二进制记录格式.下边的例子示范了在不使用zipfile
的情况下循环使用zip的头信息.H
和I
分别代表了2字节和4字节无符号数,<
标示它们使用little-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
多线程
线程是为了对没有顺序依赖的任务进行解耦.在其他任务在后台运行时,线程可以用来提高当前应用程序的响应.一个相关的使用场景就是一个线程与另一个线程并行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
对象使线程间通信和协调更容易设计,更可读,更可靠。
Logging
logging
提供了一个功能齐全并易拓展的日志系统,在这个例子中,log信息被发送到一个文件或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进行配置,或在不修改程序的前提下通过加载用户编辑的配置文件进行定制配置
弱引用
Python的自动内存管理(对多数对象进行引用计数,并由garbage-collection
进行循环删除)内存在最后使用后不久就被释放了。
这种方法已经对大多数程序都适用了,但有时需要追踪对象被其他对象的使用情况,不幸的是,仅仅是追踪这些对象所创建的引用就会使得这些引用变成永久性的。weakref
提供一些工具,使得当追踪这些对象时不需要创建引用,当对象不再被需要时,它被自动从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 del a # remove the one reference gc.collect() # run garbage collection right away d['primary'] # entry was automatically removed
Lists 工具
很多数据结构需求可以通过内建的list来满足,有时,也需要根据实现的不同表现进行替换。
array
提供array()
对象,它就像一个能以更紧凑地存储同质数据的list,下边的例子展示了一组2字节的无符号二进制数的存储(typecode:H),而不是常规的16字节
from array import array a = array('H', [4000, 10, 700, 22222]) sum(a) a[1:3]
collections
提供一个deque()
对象,它就像是可以在两端快速add,pop,但读取中间数据慢的list,这个对象更适合实现队列和广度优先的树
from collections import deque d = deque(["task1", "task2", "task3"]) d.append("task4") print("Handling", d.popleft()) 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)
除了替代list的实现外,这个库还提供其他工具,bisect
提供控制list排序的功能
import bisect scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500, 'python')] bisect.insort(scores, (300, 'ruby')) scores
heapq
为常见的list实现了堆功能,最小的元素总会在0位上,这对那些总需要找到最小元素但又不想每次对list进行排序的应用来说是很有用的
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
十进制浮点运算
decimal
提供Decimal
数据类型来进行十进制浮点运算,和内建的二进制浮点实现float
相比,它在以下方面更有帮助
- 需要准确表示十进制的金融或其他应用
- 精度控制
- 控制舍入以符合规定要求
- 追踪有意义的精度
- 期望匹配人工计算结果
例如,计算一个70美分的电话费用的5%的税在十进制浮点和二进制浮点上会给出不同的结果。如果结果被舍入到最近的分值,则差异变得显著:
from decimal import * round(Decimal('0.70') * Decimal('1.05'), 2) round(.70 * 1.05, 2)
Decimal 结果在尾部追加一个0,Decimal复制手工数学计算,避免了二进制无法精确表示十进制精度带来的问题
精确表示使得十进制类能够执行那些不适合二进制浮点的模运算的相等性测试:
Decimal('1.00') % Decimal('.10') 1.00 % 0.10 sum([Decimal('0.1')]*10) == Decimal('1.0') sum([0.1]*10) == 1.0
Decimal
可按需提供算术精度
getcontext().prec = 36 Decimal(1) / Decimal(7)
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/63597.html