/
Python Standard Library
翻译: Python 江湖群
2008-03-28 13:11:52
目录
[index.html 返回首页]
1. 更多标准模块
- "Now, imagine that your friend kept complaining that she didn't want to visit you since she found it too hard to climb up the drain pipe, and you kept telling her to use the friggin' stairs like everyone else..." - eff-bot, June 1998
1.1. 概览
本章叙述了许多在 Python 程序中广泛使用的模块. 当然, 在大型的 Python 程序中不使用这些模块也是可以的, 但如果使用会节省你不少时间.
1.1.1. 文件与流
fileinput 模块可以让你更简单地向不同的文件写入内容. 该模块提供了一个简单的封装类, 一个简单的 for-in 语句就可以循环得到一个或多个文本文件的内容.
StringIO 模块 (以及 cStringIO 模块, 作为一个的变种) 实现了一个工作在内存的文件对象. 你可以在很多地方用 StringIO 对象替换普通的文件对象.
1.1.2. 类型封装
UserDict , UserList , 以及 UserString 是对应内建类型的顶层简单封装. 和内建类型不同的是, 这些封装是可以被继承的. 这在你需要一个和内建类型行为相似但由额外新方法的类的时候很有用.
1.1.3. 随机数字
random 模块提供了一些不同的随机数字生成器. whrandom 模块与此相似, 但允许你创建多个生成器对象.
[!Feather 注: whrandom 在版本 2.1 时声明不支持. 请使用 random 替代.]
1.1.4. 加密算法
md5 和 sha 模块用于计算密写的信息标记( cryptographically strong message signatures , 所谓的 "message digests", 信息摘要).
crypt 模块实现了 DES 样式的单向加密. 该模块只在 Unix 系统下可用.
rotor 模块提供了简单的双向加密. 版本 2.4 以后的朋友可以不用忙活了.
[!Feather 注: 它在版本 2.3 时申明不支持, 因为它的加密运算不安全.]
1.2. fileinput 模块
fileinput 模块允许你循环一个或多个文本文件的内容, 如 所示.
1.2.0.1. Example 2-1. 使用 fileinput 模块循环一个文本文件
File: fileinput-example-1.pyimport fileinputimport sysfor line in fileinput.input("samples/sample.txt"): sys.stdout.write("-> ") sys.stdout.write(line)*B*-> We will perhaps eventually be writing only small-> modules which are identified by name as they are-> used to build larger ones, so that devices like-> indentation, rather than delimiters, might become-> feasible for expressing local structure in the-> source language.-> -- Donald E. Knuth, December 1974*b*
你也可以使用 fileinput 模块获得当前行的元信息 (meta information). 其中包括 isfirstline , filename , lineno , 如 所示.
1.2.0.2. Example 2-2. 使用 fileinput 模块处理多个文本文件
File: fileinput-example-2.pyimport fileinputimport globimport string, sysfor line in fileinput.input(glob.glob("samples/*.txt")): if fileinput.isfirstline(): # first in a file? sys.stderr.write("-- reading %s --\n" % fileinput.filename()) sys.stdout.write(str(fileinput.lineno()) + " " + string.upper(line))*B*-- reading samples\sample.txt --1 WE WILL PERHAPS EVENTUALLY BE WRITING ONLY SMALL2 MODULES WHICH ARE IDENTIFIED BY NAME AS THEY ARE3 USED TO BUILD LARGER ONES, SO THAT DEVICES LIKE4 INDENTATION, RATHER THAN DELIMITERS, MIGHT BECOME5 FEASIBLE FOR EXPRESSING LOCAL STRUCTURE IN THE6 SOURCE LANGUAGE.7 -- DONALD E. KNUTH, DECEMBER 1974*b*
文本文件的替换操作很简单. 只需要把 inplace 关键字参数设置为 1 , 传递给 input 函数, 该模块会帮你做好一切. 展示了这些.
1.2.0.3. Example 2-3. 使用 fileinput 模块将 CRLF 改为 LF
File: fileinput-example-3.pyimport fileinput, sysfor line in fileinput.input(inplace=1): # convert Windows/DOS text files to Unix files if line[-2:] == "\r\n": line = line[:-2] + "\n" sys.stdout.write(line)
1.3. shutil 模块
shutil 实用模块包含了一些用于复制文件和文件夹的函数. 中使用的 copy 函数使用和 Unix 下 cp 命令基本相同的方式复制一个文件.
1.3.0.1. Example 2-4. 使用 shutil 复制文件
File: shutil-example-1.pyimport shutilimport osfor file in os.listdir("."): if os.path.splitext(file)[1] == ".py": print file shutil.copy(file, os.path.join("backup", file))*B*aifc-example-1.pyanydbm-example-1.pyarray-example-1.py...*b*
copytree 函数用于复制整个目录树 (与 cp -r 相同), 而 rmtree 函数用于删除整个目录树 (与 rm -r ). 如 所示.
1.3.0.2. Example 2-5. 使用 shutil 模块复制/删除目录树
File: shutil-example-2.pyimport shutilimport osSOURCE = "samples"BACKUP = "samples-bak"# create a backup directoryshutil.copytree(SOURCE, BACKUP)print os.listdir(BACKUP)# remove itshutil.rmtree(BACKUP)print os.listdir(BACKUP)*B*['sample.wav', 'sample.jpg', 'sample.au', 'sample.msg', 'sample.tgz',...Traceback (most recent call last): File "shutil-example-2.py", line 17, in ? print os.listdir(BACKUP)os.error: No such file or directory*b*
1.4. tempfile 模块
中展示的 tempfile 模块允许你快速地创建名称唯一的临时文件供使用.
1.4.0.1. Example 2-6. 使用 tempfile 模块创建临时文件
File: tempfile-example-1.pyimport tempfileimport ostempfile = tempfile.mktemp()print "tempfile", "=>", tempfilefile = open(tempfile, "w+b")file.write("*" * 1000)file.seek(0)print len(file.read()), "bytes"file.close()try: # must remove file when done os.remove(tempfile)except OSError: passtempfile => C:\TEMP\~160-11000 bytes
TemporaryFile 函数会自动挑选合适的文件名, 并打开文件, 如 所示. 而且它会确保该文件在关闭的时候会被删除. (在 Unix 下, 你可以删除一个已打开的文件, 这 时文件关闭时它会被自动删除. 在其他平台上, 这通过一个特殊的封装类实现.)
1.4.0.2. Example 2-7. 使用 tempfile 模块打开临时文件
File: tempfile-example-2.pyimport tempfilefile = tempfile.TemporaryFile()for i in range(100): file.write("*" * 100)file.close() # removes the file!
1.5. StringIO 模块
展示了 StringIO 模块的使用. 它实现了一个工作在内存的文件对象 (内存文件). 在大多需要标准文件对象的地方都可以使用它来替换.
1.5.0.1. Example 2-8. 使用 StringIO 模块从内存文件读入内容
File: stringio-example-1.pyimport StringIOMESSAGE = "That man is depriving a village somewhere of a computer scientist."file = StringIO.StringIO(MESSAGE)print file.read()*B*That man is depriving a village somewhere of a computer scientist.*b*
StringIO 类实现了内建文件对象的所有方法, 此外还有 getvalue 方法用来返回它内部的字符串值. 展示了这个方法.
1.5.0.2. Example 2-9. 使用 StringIO 模块向内存文件写入内容
File: stringio-example-2.pyimport StringIOfile = StringIO.StringIO()file.write("This man is no ordinary man. ")file.write("This is Mr. F. G. Superman.")print file.getvalue()*B*This man is no ordinary man. This is Mr. F. G. Superman.*b*
StringIO 可以用于重新定向 Python 解释器的输出, 如 所示.
1.5.0.3. Example 2-10. 使用 StringIO 模块捕获输出
File: stringio-example-3.pyimport StringIOimport string, sysstdout = sys.stdoutsys.stdout = file = StringIO.StringIO()print """According to Gbaya folktales, trickery and guileare the best ways to defeat the python, king ofsnakes, which was hatched from a dragon at theworld's start. -- National Geographic, May 1997"""sys.stdout = stdoutprint string.upper(file.getvalue())*B*ACCORDING TO GBAYA FOLKTALES, TRICKERY AND GUILEARE THE BEST WAYS TO DEFEAT THE PYTHON, KING OFSNAKES, WHICH WAS HATCHED FROM A DRAGON AT THEWORLD'S START. -- NATIONAL GEOGRAPHIC, MAY 1997*b*
1.6. cStringIO 模块
cStringIO 是一个可选的模块, 是 StringIO 的更快速实现. 它的工作方式和 StringIO 基本相同, 但是它不可以被继承. 展示了 cStringIO 的用法, 另参考前一节.
1.6.0.1. Example 2-11. 使用 cStringIO 模块
File: cstringio-example-1.pyimport cStringIOMESSAGE = "That man is depriving a village somewhere of a computer scientist."file = cStringIO.StringIO(MESSAGE)print file.read()*B*That man is depriving a village somewhere of a computer scientist.*b*
为了让你的代码尽可能快, 但同时保证兼容低版本的 Python ,你可以使用一个小技巧在 cStringIO 不可用时启用 StringIO 模块, 如 所示.
1.6.0.2. Example 2-12. 后退至 StringIO
File: cstringio-example-2.pytry: import cStringIO StringIO = cStringIOexcept ImportError: import StringIOprint StringIO*B**b*
1.7. mmap 模块
(2.0 新增) mmap 模块提供了操作系统内存映射函数的接口, 如 所示. 映射区域的行为和字符串对象类似, 但数据是直接从文件读取的.
1.7.0.1. Example 2-13. 使用 mmap 模块
File: mmap-example-1.pyimport mmapimport osfilename = "samples/sample.txt"file = open(filename, "r+")size = os.path.getsize(filename)data = mmap.mmap(file.fileno(), size)# basicsprint dataprint len(data), size# use slicing to read from the file# 使用切片操作读取文件print repr(data[:10]), repr(data[:10])# or use the standard file interface# 或使用标准的文件接口print repr(data.read(10)), repr(data.read(10))*B*302 302'We will pe' 'We will pe''We will pe' 'rhaps even'*b*
在 Windows 下, 这个文件必须以既可读又可写的模式打开( r+ , w+ , 或 a+ ), 否则 mmap 调用会失败.
[!Feather 注: 经本人测试, a+ 模式是完全可以的, 原文只有 r+ 和 w+]
展示了内存映射区域的使用, 在很多地方它都可以替换普通字符串使用, 包括正则表达式和其他字符串操作.
1.7.0.2. Example 2-14. 对映射区域使用字符串方法和正则表达式
File: mmap-example-2.pyimport mmapimport os, string, redef mapfile(filename): file = open(filename, "r+") size = os.path.getsize(filename) return mmap.mmap(file.fileno(), size)data = mapfile("samples/sample.txt")# searchindex = data.find("small")print index, repr(data[index-5:index+15])# regular expressions work too!m = re.search("small", data)print m.start(), m.group()*B*43 'only small\015\012modules '43 small*b*
1.8. UserDict 模块
UserDict 模块包含了一个可继承的字典类 (事实上是对内建字典类型的 Python 封装).
展示了一个增强的字典类, 允许对字典使用 "加/+" 操作并提供了接受关键字参数的构造函数.
1.8.0.1. Example 2-15. 使用 UserDict 模块
File: userdict-example-1.pyimport UserDictclass FancyDict(UserDict.UserDict): def _ _init_ _(self, data = {}, **kw): UserDict.UserDict._ _init_ _(self) self.update(data) self.update(kw) def _ _add_ _(self, other): dict = FancyDict(self.data) dict.update(b) return dicta = FancyDict(a = 1)b = FancyDict(b = 2)print a + b*B*{'b': 2, 'a': 1}*b*
1.9. UserList 模块
UserList 模块包含了一个可继承的列表类 (事实上是对内建列表类型的 Python 封装).
在 中, 实例类似一个普通的列表对象, 但它允许你通过赋值为列表添加项目.
1.9.0.1. Example 2-16. 使用 UserList 模块
File: userlist-example-1.pyimport UserListclass AutoList(UserList.UserList): def _ _setitem_ _(self, i, item): if i == len(self.data): self.data.append(item) else: self.data[i] = itemlist = AutoList()for i in range(10): list[i] = iprint list*B*[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]*b*
1.10. UserString 模块
(2.0 新增) UserString 模块包含两个类, 和 . 前者是对标准字符串类型的封装, 后者是一个变种, 允许你修改特定位置的字符(联想下列表就知道了).
注意 并不是效率很好, 许多操作是通过切片和字符串连接实现的. 如果性能很对你的脚本来说重要的话, 你最好使用字符串片断的列表或者 array 模块. 展示了 UserString 模块.
1.10.0.1. Example 2-17. 使用 UserString 模块
File: userstring-example-1.pyimport UserStringclass MyString(UserString.MutableString): def append(self, s): self.data = self.data + s def insert(self, index, s): self.data = self.data[index:] + s + self.data[index:] def remove(self, s): self.data = self.data.replace(s, "")file = open("samples/book.txt")text = file.read()file.close()book = MyString(text)for bird in ["gannet", "robin", "nuthatch"]: book.remove(bird)print book*B*...C: The one without the !P: The one without the -!!! They've ALL got the !! It's aStandard British Bird, the , it's in all the books!!!...*b*
1.11. traceback 模块
展示了 traceback 模块允许你在程序里打印异常的跟踪返回 (Traceback)信息, 类似未捕获异常时解释器所做的. 如 所示.
1.11.0.1. Example 2-18. 使用 traceback 模块打印跟踪返回信息
File: traceback-example-1.py# note! importing the traceback module messes up the# exception state, so you better do that here and not# in the exception handler# 注意! 导入 traceback 会清理掉异常状态, 所以# 最好别在异常处理代码中导入该模块import tracebacktry: raise SyntaxError, "example"except: traceback.print_exc()*B*Traceback (innermost last): File "traceback-example-1.py", line 7, in ?SyntaxError: example*b*
使用 StringIO 模块将跟踪返回信息放在字符串中.
1.11.0.2. Example 2-19. 使用 traceback 模块将跟踪返回信息复制到字符串
File: traceback-example-2.pyimport tracebackimport StringIOtry: raise IOError, "an i/o error occurred"except: fp = StringIO.StringIO() traceback.print_exc(file=fp) message = fp.getvalue() print "failure! the error was:", repr(message)*B*failure! the error was: 'Traceback (innermost last):\012 File"traceback-example-2.py", line 5, in ?\012IOError: an i/o erroroccurred\012'*b*
你可以使用 extract_tb 函数格式化跟踪返回信息, 得到包含错误信息的列表, 如 所示.
1.11.0.3. Example 2-20. 使用 traceback Module 模块编码 Traceback 对象
File: traceback-example-3.pyimport tracebackimport sysdef function(): raise IOError, "an i/o error occurred"try: function()except: info = sys.exc_info() for file, lineno, function, text in traceback.extract_tb(info[2]): print file, "line", lineno, "in", function print "=>", repr(text) print "** %s: %s" % info[:2]*B*traceback-example-3.py line 8 in ?=> 'function()'traceback-example-3.py line 5 in function=> 'raise IOError, "an i/o error occurred"'** exceptions.IOError: an i/o error occurred*b*
1.12. errno 模块
errno 模块定义了许多的符号错误码, 比如 ENOENT ("没有该目录入口") 以及 EPERM ("权限被拒绝"). 它还提供了一个映射到对应平台数字错误代码的字典. 展示了如何使用 errno 模块.
在大多情况下, IOError 异常会提供一个二元元组, 包含对应数值错误代码和一个说明字符串. 如果你需要区分不同的错误代码, 那么最好在可能的地方使用符号名称.
1.12.0.1. Example 2-21. 使用 errno 模块
File: errno-example-1.pyimport errnotry: fp = open("no.such.file")except IOError, (error, message): if error == errno.ENOENT: print "no such file" elif error == errno.EPERM: print "permission denied" else: print message*B*no such file*b*
绕了些无用的弯子, 不过它很好地说明了如何使用 errorcode 字典把数字错误码映射到符号名称( symbolic name ).
1.12.0.2. Example 2-22. 使用 errorcode 字典
File: errno-example-2.pyimport errnotry: fp = open("no.such.file")except IOError, (error, message): print error, repr(message) print errno.errorcode[error]# 2 'No such file or directory'# ENOENT
1.13. getopt 模块
getopt 模块包含用于抽出命令行选项和参数的函数, 它可以处理多种格式的选项. 如 所示.
其中第 2 个参数指定了允许的可缩写的选项. 选项名后的冒号(:) 意味这这个选项必须有额外的参数.
1.13.0.1. Example 2-23. 使用 getopt 模块
File: getopt-example-1.pyimport getoptimport sys# simulate command-line invocation# 模仿命令行参数sys.argv = ["myscript.py", "-l", "-d", "directory", "filename"]# process options# 处理选项opts, args = getopt.getopt(sys.argv[1:], "ld:")long = 0directory = Nonefor o, v in opts: if o == "-l": long = 1 elif o == "-d": directory = vprint "long", "=", longprint "directory", "=", directoryprint "arguments", "=", args*B*long = 1directory = directoryarguments = ['filename']*b*
为了让 getopt 查找长的选项, 如 所示, 传递一个描述选项的列表做为第 3 个参数. 如果一个选项名称以等号(=) 结尾, 那么它必须有一个附加参数.
1.13.0.2. Example 2-24. 使用 getopt 模块处理长选项
File: getopt-example-2.pyimport getoptimport sys# simulate command-line invocation# 模仿命令行参数sys.argv = ["myscript.py", "--echo", "--printer", "lp01", "message"]opts, args = getopt.getopt(sys.argv[1:], "ep:", ["echo", "printer="])# process options# 处理选项echo = 0printer = Nonefor o, v in opts: if o in ("-e", "--echo"): echo = 1 elif o in ("-p", "--printer"): printer = vprint "echo", "=", echoprint "printer", "=", printerprint "arguments", "=", args*B*echo = 1printer = lp01arguments = ['message']*b*
[!Feather 注: 我不知道大家明白没, 可以自己试下:myscript.py -e -p lp01 messagemyscript.py --echo --printer=lp01 message]
1.14. getpass 模块
getpass 模块提供了平台无关的在命令行下输入密码的方法. 如 所示.
getpass(prompt) 会显示提示字符串, 关闭键盘的屏幕反馈, 然后读取密码. 如果提示参数省略, 那么它将打印出 "Password:".
getuser() 获得当前用户名, 如果可能的话.
1.14.0.1. Example 2-25. 使用 getpass 模块
File: getpass-example-1.pyimport getpassusr = getpass.getuser()pwd = getpass.getpass("enter password for user %s: " % usr)print usr, pwd*B*enter password for user mulder:mulder trustno1*b*
1.15. glob 模块
glob 根据给定模式生成满足该模式的文件名列表, 和 Unix shell 相同.
这里的模式和正则表达式类似, 但更简单. 星号(*) 匹配零个或更多个字符, 问号(?) 匹配单个字符. 你也可以使用方括号来指定字符范围, 例如 [0-9] 代表一个数字. 其他所有字符都代表它们本身.
glob(pattern) 返回满足给定模式的所有文件的列表. 展示了它的用法.
1.15.0.1. Example 2-26. 使用 glob 模块
File: glob-example-1.pyimport globfor file in glob.glob("samples/*.jpg"): print filesamples/sample.jpg
注意这里的 glob 返回完整路径名, 这点和 os.listdir 函数不同. glob 事实上使用了 fnmatch 模块来完成模式匹配.
1.16. fnmatch 模块
fnmatch 模块使用模式来匹配文件名. 如 所示.
模式语法和 Unix shell 中所使用的相同. 星号(*) 匹配零个或更多个字符, 问号(?) 匹配单个字符. 你也可以使用方括号来指定字符范围, 例如 [0-9] 代表一个数字. 其他所有字符都匹配它们本身.
1.16.0.1. Example 2-27. 使用 fnmatch 模块匹配文件
File: fnmatch-example-1.pyimport fnmatchimport osfor file in os.listdir("samples"): if fnmatch.fnmatch(file, "*.jpg"): print file*B*sample.jpg*b*
中的 translate 函数可以将一个文件匹配模式转换为正则表达式.
1.16.0.2. Example 2-28. 使用 fnmatch 模块将模式转换为正则表达式
File: fnmatch-example-2.pyimport fnmatchimport os, repattern = fnmatch.translate("*.jpg")for file in os.listdir("samples"): if re.match(pattern, file): print fileprint "(pattern was %s)" % pattern*B*sample.jpg(pattern was .*\.jpg$)*b*
glob 和 find 模块在内部使用 fnmatch 模块来实现.
1.17. random 模块
- "Anyone who considers arithmetical methods of producing random digits is, of course, in a state of sin." - John von Neumann, 1951
random 模块包含许多随机数生成器.
基本随机数生成器(基于 Wichmann 和 Hill , 1982 的数学运算理论) 可以通过很多方法访问, 如 所示.
1.17.0.1. Example 2-29. 使用 random 模块获得随机数字
File: random-example-1.pyimport randomfor i in range(5): # random float: 0.0 <= number < 1.0 print random.random(), # random float: 10 <= number < 20 print random.uniform(10, 20), # random integer: 100 <= number <= 1000 print random.randint(100, 1000), # random integer: even numbers in 100 <= number < 1000 print random.randrange(100, 1000, 2)*B*0.946842713956 19.5910069381 709 1720.573613195398 16.2758417025 407 1200.363241598013 16.8079747714 916 5800.602115173978 18.386796935 531 7740.526767588533 18.0783794596 223 344*b*
注意这里的 randint 函数可以返回上界, 而其他函数总是返回小于上界的值. 所有函数都有可能返回下界值.
展示了 choice 函数, 它用来从一个序列里分拣出一个随机项目. 它可以用于列表, 元组, 以及其他序列(当然, 非空的).
1.17.0.2. Example 2-30. 使用 random 模块从序列取出随机项
File: random-example-2.pyimport random# random choice from a listfor i in range(5): print random.choice([1, 2, 3, 5, 9])*B*23191*b*
在 2.0 及以后版本, shuffle 函数可以用于打乱一个列表的内容 (也就是生成一个该列表的随机全排列). 展示了如何在旧版本中实现该函数.
1.17.0.3. Example 2-31. 使用 random 模块打乱一副牌
File: random-example-4.pyimport randomtry: # available in 2.0 and later shuffle = random.shuffleexcept AttributeError: def shuffle(x): for i in xrange(len(x)-1, 0, -1): # pick an element in x[:i+1] with which to exchange x[i] j = int(random.random() * (i+1)) x[i], x[j] = x[j], x[i]cards = range(52)shuffle(cards)myhand = cards[:5]print myhand*B*[4, 8, 40, 12, 30]*b*
random 模块也包含了非恒定分布的随机生成器函数. 使用了 gauss (高斯)函数来生成满足高斯分的布随机数字.
1.17.0.4. Example 2-32. 使用 random 模块生成高斯分布随机数
File: random-example-3.pyimport randomhistogram = [0] * 20# calculate histogram for gaussian# noise, using average=5, stddev=1for i in range(1000): i = int(random.gauss(5, 1) * 2) histogram[i] = histogram[i] + 1# print the histogramm = max(histogram)for v in histogram: print "*" * (v * 50 / m)*B***************************************************************************************************************************************************************************************************************************************************************b*
你可以在 Python Library Reference 找到更多关于非恒定分布随机生成器函数的信息.
- Note*标准库中提供的随机数生成器都是伪随机数生成器. 不过这对于很多目的来说已经足够了, 比如模拟, 数值分析, 以及游戏. 可以确定的是它不适合密码学用途.*note*
1.18. whrandom 模块
*Alert*这个模块早在 2.1 就被声明不赞成, 早废了. 请使用 random 代替. @@ - Feather*alert*
展示了 whrandom , 它提供了一个伪随机数生成器. (基于 Wichmann 和 Hill, 1982 的数学运算理论). 除非你需要不共享状态的多个生成器(如多线程程序), 请使用 random 模块代替.
1.18.0.1. Example 2-33. 使用 whrandom 模块
File: whrandom-example-1.pyimport whrandom# same as randomprint whrandom.random()print whrandom.choice([1, 2, 3, 5, 9])print whrandom.uniform(10, 20)print whrandom.randint(100, 1000)*B*0.113412062346116.8778954689799*b*
展示了如何使用 whrandom 类实例创建多个生成器.
1.18.0.2. Example 2-34. 使用 whrandom 模块创建多个随机生成器
File: whrandom-example-2.pyimport whrandom# initialize all generators with the same seedrand1 = whrandom.whrandom(4,7,11)rand2 = whrandom.whrandom(4,7,11)rand3 = whrandom.whrandom(4,7,11)for i in range(5): print rand1.random(), rand2.random(), rand3.random()*B*0.123993532536 0.123993532536 0.1239935325360.180951499518 0.180951499518 0.1809514995180.291924111809 0.291924111809 0.2919241118090.952048889363 0.952048889363 0.9520488893630.969794283643 0.969794283643 0.969794283643*b*
1.19. md5 模块
md5 (Message-Digest Algorithm 5)模块用于计算信息密文(信息摘要).
md5 算法计算一个强壮的128位密文. 这意味着如果两个字符串是不同的, 那么有极高可能它们的 md5 也不同. 也就是说, 给定一个 md5 密文, 那么几乎没有可能再找到另个字符串的密文与此相同. 展示了如何使用 md5 模块.
1.19.0.1. Example 2-35. 使用 md5 模块
File: md5-example-1.pyimport md5hash = md5.new()hash.update("spam, spam, and eggs")print repr(hash.digest())*B* 'L\005J\243\266\355\243u`\305r\203\267\020F\303'*b*
注意这里的校验和是一个二进制字符串. 展示了如何获得一个十六进制或 base64 编码的字符串.
1.19.0.2. Example 2-36. 使用 md5 模块获得十六进制或 base64 编码的 md5 值
File: md5-example-2.pyimport md5import stringimport base64hash = md5.new()hash.update("spam, spam, and eggs")value = hash.digest()print hash.hexdigest()# before 2.0, the above can be written as# 在 2.0 前, 以上应该写做:# print string.join(map(lambda v: "%02x" % ord(v), value), "")print base64.encodestring(value)*B*4c054aa3b6eda37560c57283b71046c3TAVKo7bto3VgxXKDtxBGww==*b*
展示了如何使用 md5 校验和来处理口令的发送与应答的验证(不过我们将稍候讨论这里使用随机数字所带来的问题).
1.19.0.3. Example 2-37. 使用 md5 模块来处理口令的发送与应答的验证
File: md5-example-3.pyimport md5import string, randomdef getchallenge(): # generate a 16-byte long random string. (note that the built- # in pseudo-random generator uses a 24-bit seed, so this is not # as good as it may seem...) # 生成一个 16 字节长的随机字符串. 注意内建的伪随机生成器 # 使用的是 24 位的种子(seed), 所以这里这样用并不好.. challenge = map(lambda i: chr(random.randint(0, 255)), range(16)) return string.join(challenge, "")def getresponse(password, challenge): # calculate combined digest for password and challenge # 计算密码和质询(challenge)的联合密文 m = md5.new() m.update(password) m.update(challenge) return m.digest()## server/client communication# 服务器/客户端通讯# 1. client connects. server issues challenge.# 1. 客户端连接, 服务器发布质询(challenge)print "client:", "connect"challenge = getchallenge()print "server:", repr(challenge)# 2. client combines password and challenge, and calculates# the response.# 2. 客户端计算密码和质询(challenge)的组合后的密文client_response = getresponse("trustno1", challenge)print "client:", repr(client_response)# 3. server does the same, and compares the result with the# client response. the result is a safe login in which the# password is never sent across the communication channel.# 3. 服务器做同样的事, 然后比较结果与客户端的返回, # 判断是否允许用户登陆. 这样做密码没有在通讯中明文传输.server_response = getresponse("trustno1", challenge)if server_response == client_response: print "server:", "login ok"*B*client: connectserver: '\334\352\227Z#\272\273\212KG\330\265\032>\311o'client: "l'\305\240-x\245\237\035\225A\254\233\337\225\001"server: login ok*b*
提供了 md5 的一个变种, 你可以通过标记信息来判断它是否在网络传输过程中被修改(丢失).
1.19.0.4. Example 2-38. 使用 md5 模块检查数据完整性
File: md5-example-4.pyimport md5import arrayclass HMAC_MD5: # keyed md5 message authentication def _ _init_ _(self, key): if len(key) > 64: key = md5.new(key).digest() ipad = array.array("B", [0x36] * 64) opad = array.array("B", [0x5C] * 64) for i in range(len(key)): ipad[i] = ipad[i] ^ ord(key[i]) opad[i] = opad[i] ^ ord(key[i]) self.ipad = md5.md5(ipad.tostring()) self.opad = md5.md5(opad.tostring()) def digest(self, data): ipad = self.ipad.copy() opad = self.opad.copy() ipad.update(data) opad.update(ipad.digest()) return opad.digest()## simulate server end# 模拟服务器端key = "this should be a well-kept secret"message = open("samples/sample.txt").read()signature = HMAC_MD5(key).digest(message)# (send message and signature across a public network)# (经过由网络发送信息和签名)## simulate client end#模拟客户端key = "this should be a well-kept secret"client_signature = HMAC_MD5(key).digest(message)if client_signature == signature: print "this is the original message:" print print messageelse: print "someone has modified the message!!!"
copy 方法会对这个内部对象状态做一个快照( snapshot ). 这允许你预先计算部分密文摘要(例如 中的 padded key).
该算法的细节请参阅 HMAC-MD5:Keyed-MD5 for Message Authentication ( ) by Krawczyk, 或其他.
- Note*千万别忘记内建的伪随机生成器对于加密操作而言并不合适. 千万小心. *note*
1.20. sha 模块
sha 模块提供了计算信息摘要(密文)的另种方法, 如 所示. 它与 md5 模块类似, 但生成的是 160 位签名.
1.20.0.1. Example 2-39. 使用 sha 模块
File: sha-example-1.pyimport shahash = sha.new()hash.update("spam, spam, and eggs")print repr(hash.digest())print hash.hexdigest()*B*'\321\333\003\026I\331\272-j\303\247\240\345\343Tvq\364\346\311'd1db031649d9ba2d6ac3a7a0e5e3547671f4e6c9*b*
关于 sha 密文的使用, 请参阅 md5 中的例子.
1.21. crypt 模块
(可选, 只用于 Unix) crypt 模块实现了单向的 DES 加密, Unix 系统使用这个加密算法来储存密码, 这个模块真正也就只在检查这样的密码时有用.
展示了如何使用 crypt.crypt 来加密一个密码, 将密码和 salt 组合起来然后传递给函数, 这里的 salt 包含两位随机字符. 现在你可以扔掉原密码而只保存加密后的字符串了.
1.21.0.1. Example 2-40. 使用 crypt 模块
File: crypt-example-1.pyimport cryptimport random, stringdef getsalt(chars = string.letters + string.digits): # generate a random 2-character 'salt' # 生成随机的 2 字符 'salt' return random.choice(chars) + random.choice(chars)print crypt.crypt("bananas", getsalt())*B*'py8UGrijma1j6'*b*
确认密码时, 只需要用新密码调用加密函数, 并取加密后字符串的前两位作为 salt 即可. 如 果结果和加密后字符串匹配, 那么密码就是正确的. 使用 pwd 模块来获取已知用户的加密后密码.
1.21.0.2. Example 2-41. 使用 crypt 模块身份验证
File: crypt-example-2.pyimport pwd, cryptdef login(user, password): "Check if user would be able to log in using password" try: pw1 = pwd.getpwnam(user)[1] pw2 = crypt.crypt(password, pw1[:2]) return pw1 == pw2 except KeyError: return 0 # no such useruser = raw_input("username:")password = raw_input("password:")if login(user, password): print "welcome", userelse: print "login failed"
关于其他实现验证的方法请参阅 md5 模块一节.
1.22. rotor 模块
*Alert*这个模块在 2.3 时被声明不赞成, 2.4 时废了. 因为它的加密算法不安全. @@ - Feather*alert*
(可选) rotor 模块实现了一个简单的加密算法. 如 所示. 它的算法基于 WWII Enigma engine.
1.22.0.1. Example 2-42. 使用 rotor 模块
File: rotor-example-1.pyimport rotorSECRET_KEY = "spam"MESSAGE = "the holy grail"r = rotor.newrotor(SECRET_KEY)encoded_message = r.encrypt(MESSAGE)decoded_message = r.decrypt(encoded_message)print "original:", repr(MESSAGE)print "encoded message:", repr(encoded_message)print "decoded message:", repr(decoded_message)*B*original: 'the holy grail'encoded message: '\227\271\244\015\305sw\3340\337\252\237\340U'decoded message: 'the holy grail'*b*
1.23. zlib 模块
(可选) zlib 模块为 "zlib" 压缩提供支持. (这种压缩方法是 "deflate".)
展示了如何使用 compress 和 decompress 函数接受字符串参数.
1.23.0.1. Example 2-43. 使用 zlib 模块压缩字符串
File: zlib-example-1.pyimport zlibMESSAGE = "life of brian"compressed_message = zlib.compress(MESSAGE)decompressed_message = zlib.decompress(compressed_message)print "original:", repr(MESSAGE)print "compressed message:", repr(compressed_message)print "decompressed message:", repr(decompressed_message)*B*original: 'life of brian'compressed message: 'x\234\313\311LKU\310OSH*\312L\314\003\000!\010\004\302'decompressed message: 'life of brian'*b*
文件的内容决定了压缩比率, 说明了这点.
1.23.0.2. Example 2-44. 使用 zlib 模块压缩多个不同类型文件
File: zlib-example-2.pyimport zlibimport globfor file in glob.glob("samples/*"): indata = open(file, "rb").read() outdata = zlib.compress(indata, zlib.Z_BEST_COMPRESSION) print file, len(indata), "=>", len(outdata), print "%d%%" % (len(outdata) * 100 / len(indata))*B*samples\sample.au 1676 => 1109 66%samples\sample.gz 42 => 51 121%samples\sample.htm 186 => 135 72%samples\sample.ini 246 => 190 77%samples\sample.jpg 4762 => 4632 97%samples\sample.msg 450 => 275 61%samples\sample.sgm 430 => 321 74%samples\sample.tar 10240 => 125 1%samples\sample.tgz 155 => 159 102%samples\sample.txt 302 => 220 72%samples\sample.wav 13260 => 10992 82%*b*
你也可以实时地压缩或解压缩数据, 如 所示.
1.23.0.3. Example 2-45. 使用 zlib 模块解压缩流
File: zlib-example-3.pyimport zlibencoder = zlib.compressobj()data = encoder.compress("life")data = data + encoder.compress(" of ")data = data + encoder.compress("brian")data = data + encoder.flush()print repr(data)print repr(zlib.decompress(data))*B*'x\234\313\311LKU\310OSH*\312L\314\003\000!\010\004\302''life of brian'*b*
把解码对象封装到了一个类似文件对象的类中, 实现了一些文件对象的方法, 这样使得读取压缩文件更方便.
1.23.0.4. Example 2-46. 压缩流的仿文件访问方式
File: zlib-example-4.pyimport zlibimport string, StringIOclass ZipInputStream: def _ _init_ _(self, file): self.file = file self._ _rewind() def _ _rewind(self): self.zip = zlib.decompressobj() self.pos = 0 # position in zipped stream self.offset = 0 # position in unzipped stream self.data = "" def _ _fill(self, bytes): if self.zip: # read until we have enough bytes in the buffer while not bytes or len(self.data) < bytes: self.file.seek(self.pos) data = self.file.read(16384) if not data: self.data = self.data + self.zip.flush() self.zip = None # no more data break self.pos = self.pos + len(data) self.data = self.data + self.zip.decompress(data) def seek(self, offset, whence=0): if whence == 0: position = offset elif whence == 1: position = self.offset + offset else: raise IOError, "Illegal argument" if position < self.offset: raise IOError, "Cannot seek backwards" # skip forward, in 16k blocks while position > self.offset: if not self.read(min(position - self.offset, 16384)): break def tell(self): return self.offset def read(self, bytes = 0): self._ _fill(bytes) if bytes: data = self.data[:bytes] self.data = self.data[bytes:] else: data = self.data self.data = "" self.offset = self.offset + len(data) return data def readline(self): # make sure we have an entire line while self.zip and "\n" not in self.data: self._ _fill(len(self.data) + 512) i = string.find(self.data, "\n") + 1 if i <= 0: return self.read() return self.read(i) def readlines(self): lines = [] while 1: s = self.readline() if not s: break lines.append(s) return lines## try it outdata = open("samples/sample.txt").read()data = zlib.compress(data)file = ZipInputStream(StringIO.StringIO(data))for line in file.readlines(): print line[:-1]*B*We will perhaps eventually be writing only smallmodules which are identified by name as they areused to build larger ones, so that devices likeindentation, rather than delimiters, might becomefeasible for expressing local structure in thesource language. -- Donald E. Knuth, December 1974*b*
1.24. code 模块
code 模块提供了一些用于模拟标准交互解释器行为的函数.
compile_command 与内建 compile 函数行为相似, 但它会通过测试来保证你传递的是一个完成的 Python 语句.
在 中, 我们一行一行地编译一个程序, 编译完成后会执行所得到的代码对象 (code object). 程序代码如下:
a = ( 1, 2, 3)print a
注意只有我们到达第 2 个括号, 元组的赋值操作能编译完成.
1.24.0.1. Example 2-47. 使用 code 模块编译语句
File: code-example-1.pyimport codeimport string# SCRIPT = [ "a = (", " 1,", " 2,", " 3 ", ")", "print a"]script = ""for line in SCRIPT: script = script + line + "\n" co = code.compile_command(script, "", "exec") if co: # got a complete statement. execute it! print "-"*40 print script, print "-"*40 exec co script = ""*B*----------------------------------------a = ( 1, 2, 3 )--------------------------------------------------------------------------------print a----------------------------------------(1, 2, 3)*b*
类实现了一个交互控制台, 类似你启动的 Python 解释器交互模式.
控制台可以是活动的(自动调用函数到达下一行) 或是被动的(当有新数据时调用 push 方法). 默认使用内建的 raw_input 函数. 如果你想使用另个输入函数, 你可以使用相同的名称重载这个方法. 展示了如何使用 code 模块来模拟交互解释器.
1.24.0.2. Example 2-48. 使用 code 模块模拟交互解释器
File: code-example-2.pyimport codeconsole = code.InteractiveConsole()console.interact()*B*Python 1.5.2Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam(InteractiveConsole)>>> a = (... 1,... 2,... 3... )>>> print a(1, 2, 3)*b*
中的脚本定义了一个 keyboard 函数. 它允许你在程序中手动控制交互解释器.
1.24.0.3. Example 2-49. 使用 code 模块实现简单的 Debugging
File: code-example-3.pydef keyboard(banner=None): import code, sys # use exception trick to pick up the current frame try: raise None except: frame = sys.exc_info()[2].tb_frame.f_back # evaluate commands in current namespace namespace = frame.f_globals.copy() namespace.update(frame.f_locals) code.interact(banner=banner, local=namespace)def func(): print "START" a = 10 keyboard() print "END"func()*B*STARTPython 1.5.2Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam(InteractiveConsole)>>> print a10>>> print keyboard^ZEND*b*
PythonStandardLib/chpt2 (2009-12-25 07:15:19由localhost编辑)
- Page.execute = 0.630s
- getACL = 0.128s
- init = 0.002s
- load_multi_cfg = 0.000s
- run = 1.191s
- send_page = 1.154s
- send_page_content = 0.639s
- send_page_content|1 = 0.215s
- send_page|1 = 0.243s
- total = 1.193s