[python3教程]第十一章.标准库简介第二部分

第二部分介绍的会是一些高级的模块,主要用来支持专业的编程需求。这些模块在小型的脚本中并不常见。

11.1 Output Formatting

reprlib模块提供了一个repr函数,内建函数有一个同名的repr函数我们以前介绍过,只是reprlib模块的repr函数返回的字符串并不是完全展开的,其他方面都是一样的,他们把包含数据的容器比如列表字典什么的转换成字符串:

>>> import reprlib
>>> reprlib.repr(set('supercalifragilisticexpialidocious'))
"{'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
73
>>> 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异常。对于信件合并类的应用,用户的输入可能并不完全,这时候使用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
from string import Template
photofiles = ['img_1074.JPG', 'img_1076.JPG', 'img_1077.JPG']
class BatchRename(Template):
    delimiter = '%'

fmt = '%d%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))

输出的结果是这样的:
substitute的介绍
模版还有一个作用就是把程序的逻辑从多种格式的输出细节中分离出来,这使我们可能用定制的模版替换XML文件、纯文本或是HTML网页的报告。

11.3 Working with Binary Data Record Layouts

struct模块提供了pack()和unpack()来处理长度可变的二进制格式。接下来的例子会展示怎样在不使用zipfile模块的情况下遍历一个ZIP文件的头文件。”H“和”I“分别代表两位和四位的无符号数字。”<”作为缩进表示他们都是标准大小并且按照大小排列:

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

我们把不是顺序相关的工作分离开来,这种技术就叫做线程(感兴趣应该去看看计算机系统,我也就是大概的扫了一眼)。线程能够提升应用的相应能力,当后台有工作进行时应用可以同时接受用户的输入。一个相关的用法就是在后台计算工作进行时,同时进行数据的输入输出工作。
下面的例子将会展示我们是怎么在主程序运行的同时进行后台的工作的:

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.DEFLATED)
        f.write(self.infile)
        f.close()
        print('Finished background zip of:', self.infile)

background = AsyncZip('infile.txt', 'outfile.zip')
background.start()
print('The main program continues to run in background.')

background.join()
print('Main program waited until background was done.')

其实处理器在同一时间肯定是处理一个代码块的,这是我在计算机原理与接口这本书上看到的。
其实多线程处理要解决的最重要的问题就是协调线程间共享的数据和其他资源。为了这个目的threading模块提供了一些同步的基本类型,比如事件,环境变量,锁,信号。
虽然这些工具很强大,但是设计错误可能导致一些难以复制的问题。所以在进行协同工作的时候我们更倾向于将所有的资源集中在一个进程中,然后使用queue模块为其他的线程提供访问。使用Queue对象进行程序线程的内部交互和协同,能够让程序设计起来更简单并且已读可靠。

11.5 Logging

logging模块提供了一个功能齐全并且灵活的日志系统。最简单的情况,日志信息会被发送到一个文件或者sys.stderr

import logging
logging.debug('Debugging information')
logging.info('Informational message')
logging.warning('Warning:config file %s not found', 'server.conf')
logging.critical('Critical Error -----shut down')
WARNING:root:Warning:config file server.conf not found
CRITICAL:root:Critical error -- shutting down

默认情况下,信息类和编译类的消息是被保留住的,输出会被发送给标准错误。其他的输出选项包括电子邮件、数据报、套接字、或者HTTP服务。新的过滤器可以基于消息的属性(debug,info,warning,error,critical)来选择不同的发送方式。
日志系统可以直接由python配置,或者从别的用户编辑的日志配置文件里加在定制,不需要更换应用。

11.6 Weak References

python会自动管理内存(使用引用计数器和垃圾回收机制去消除那些占用内存的无用信息)。当所有指向一个对象的引用都消失后,这个对象所占用的内存就会被立刻释放。
这种工作方式在大多数时候都是好用的,但有时候我们引用一个对象只是因为其他内容需要,并不需要持续的使用这个对象。不行的是这样去引用一个对象会导致他一直存在。weakref模块提供了一个工具可以让我们在追踪一个对象时不引用,也就是说引用计数器不会增加数字。当这个对象不在被需要的时候,他会自动从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
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:/python36/lib/weakref.py", line 46, in __getitem__
        o = self.data[key]()
KeyError: 'primary'

11.7 Tools for Working with Lists

很多的数据结构都可以用内建的list来实现,然而有些时候我们对不同的数据交换有不同的效率要求,所以需要另外的实现方法。
array模块提供了一个array类,他可以更加紧凑地储存同类的数据。下面的这个例子中我们会用array来储存2字节的无符号二级制数,常规情况下python中每项会占16字节:

>>> from array import array
>>> a = array('H', [4000, 10, 700, 22222])
>>> sum(a)
26932
>>> a[1:3]
array('H', [10, 700])

使用分片的功能会返回一个array的子序列(哪怕分片中只有一个数据),而使用下标就会直接返回数据。这么设计是有好处的,因为array类是有迭代器的。
collections模块提供了一个deque()对象,他可以实现快速的进出栈,但是在搜索中间数据时会比较缓慢。这个对象比较适合作为队列或者广度优先树:

>>> 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实现堆。就是始终把最小值放在第一个位置上。当我们需要经常提取最小值,但是又不想要对整个列表排列时,这个结构是非常有用的:

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

decimal模块提供了一个Decimal数据类型来来做浮点数的运算。于内建的二进制浮点数相比,这个类在如下情况非常有利:

  • 金融应用或者其他对小数精度高要求的程序
  • 控制精度
  • 控制取整的规则或者管理的要求
  • 追踪重要的小数
  • 用户期待结果能和用手解出来的结果相匹配
    举个例子,我们对一个70分的手机收5%的税,二级制浮点数和decimal浮点数的结果是不一样的。如果我们保留到分的话这是一个巨大的差别:
from decimal import *
>>>round(decimal('0.7) * decimal('1.05'), 2)
decimal('0.74')
>>>round(0.7 * 1.05)
0.73

我相信税务人员肯定是用decimal的!Decimal的结果会在结尾处跟一个0,他会推断两位小数相乘会有四位小数。Decimal计算的结果就像是手算的结果一样,而且他能防止二级制浮点型出现的不准确的问题,精确的表示方式使得Decimal可以进行模运算的判断,而二进制浮点数是不能这么判断的:

>>>Decimal('1.00') % Decimal('.1')
Decimal('0.00')
>>>1.00 % .1
0.09999999999999995

>>>sum([Decimal('0.1')] * 10) == Decimal('1.0')
True
>>>sum([0.1] * 10) == 1.0
False

decimal模块能设置任何你想要的精度,当然不能超过你内存的范围:

>>>getcontext().prec = 36
>>>Decimal(1) / Deciaml(7)
Decimal('0.142857142857142857142857142857142857')
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值