Python Essential Reference 4th – 第9章 – 读书笔记

本章主要是关于各种I/O操作,包括File-Objec及其操作、Unicode字符串相关的I/O函数以及对象的序列化和持久化。

1、从cmd读取传入参数:sys.argv。其中sys.argv[0]是当前的程序名称。

2、想要退出系统时,出了exit和return外,还可以raise SystemExit(1)

3、解析命令行参数,可以使用optparse模块。
不过从2.7之后,Python将废弃optparse,转而支持argparse,话说开源的东西变动太大。。。
optparse的用法如下:

import optparse

p = optparse.OptionParser()

#Add option of -o/--output
p.add_option("-o",action="store",dest="outfile")
p.add_option("--output",action="store",dest="outfile")

#Add option of boolean
p.add_option("-d",action="store_true",dest="debug")
p.add_option("--debug",action="store_true",dest="debug")

#Set default values
#p.set_default(debug=False)

opts,args = p.parse_args()

print opts.outfile,opts.debug

4、环境变量:os.envviron

>>> import os
>>> print os.environ
{'TMP': 'C:\Users\liheyuan\AppData\Local\Temp', 'COMPUTERNAME': 'LIHEYUAN-PC', 'USERDOMAIN': 'liheyuan-PC', 'PSMODULEPATH': 'C:\Windows\system32\WindowsPowerShell\v1.0\Modules\', 'COMMONPROGRAMFILES': 'C:\Program Files\Common Files', 'PROCESSOR_IDENTIFIER': 'x86 Family 6 Model 23 Stepping 10, GenuineIntel', 'PROGRAMFILES': 'C:\Program Files', 'PROCESSOR_REVISION': '170a', 'SYSTEMROOT': 'C:\Windows', 'HOME': 'C:\Users\liheyuan', 'COMSPEC': 'C:\Windows\system32\cmd.exe', 'TK_LIBRARY': 'C:\Python27\tcl\tk8.5', 'TEMP': 'C:\Users\liheyuan\AppData\Local\Temp', 'PROCESSOR_ARCHITECTURE': 'x86', 'TIX_LIBRARY': 'C:\Python27\tcl\tix8.4.3', 'ALLUSERSPROFILE': 'C:\ProgramData', 'SESSIONNAME': 'Console', 'HOMEPATH': '\Users\liheyuan', 'USERNAME': 'liheyuan', 'LOGONSERVER': '\\LIHEYUAN-PC', 'LOCALAPPDATA': 'C:\Users\liheyuan\AppData\Local', 'PROGRAMDATA': 'C:\ProgramData', 'PYTHONPATH': '%PYTHONPATH%;d:\python;d:\python;d:\python', 'TCL_LIBRARY': 'C:\Python27\tcl\tcl8.5', 'PATH': 'C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Common Files\Thunder Network\KanKan\Codecs', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC', 'FP_NO_HOST_CHECK': 'NO', 'WINDIR': 'C:\Windows', 'APPDATA': 'C:\Users\liheyuan\AppData\Roaming', 'HOMEDRIVE': 'C:', 'SYSTEMDRIVE': 'C:', 'NUMBER_OF_PROCESSORS': '2', 'PROCESSOR_LEVEL': '6', 'OS': 'Windows_NT', 'PUBLIC': 'C:\Users\Public', 'USERPROFILE': 'C:\Users\liheyuan'}

5、File-Object。
内置的open方法:open(name,mode,bufsize),后两个参数是可选的。

mode可取r(读)、w(写)或a(追加)。默认情况下,为了解决windows和linux下对换行的差异(windows下是\r\n,linux是\n),默认会对其进行转换统一成\n,这个转换可以通过rU关闭,或者U启用。如果要用二进制模式b,则不会进行自动换行转换。如果需要及时更新,则在r或者w或者a后面加上+。

bufsize是读取、写入的缓存,0为关闭,1是行缓存,>1之后是用字节表示的读取次数。

File-Object的操作很多,比较特别需要注意的如下:
f.readline([n]),读取一行,接近n个字节时停止,n省略时就是一行。
f.readlines([size]),读取所有的行,以list形式返回,可选接近size个字节时停止。
f.close(),关闭释放资源。
f.tell(),定位当前偏移量。
f.seek(),随机访问文件的某一位置。

6、File读取完毕后,会返回None,而非抛出异常!
因此逐行读取的方法是:

while True:
    line = f.readline()
    if not line:
        break

7、在Python2中,read()返回的是8-bit的字符串,而Python3以后统一是utf-8

8、标准输入输出:sys.stdin,sys.stdout,sys.stderr。和C中是对应的。如果直接用,可以

import sys
sys.stdin.write("What's your name:")
name = sys.stdin.readline("")

9、当然如果从stdin读取,写入stdout也不用那么麻烦,用raw_input和print

import sys
name = raw_input("What's your name:")

10、print语句,尽管我们都是用它向stdout输出,但实际上它也可以用于File-Object的。

f = open("output","w")
print >>f,"hello world"

11、print语句在使用,隔开之后,默认的分隔符是空格,我们可以改变的:
(但是我一直没执行成功的说。。。)

f = open("output","w")
print("The values are",x,y,z,sep=",")

12、print的字符串中的变量可使用“模板替换”。一般来说,web框架都有自己定义的一套模板语法和文件,但基本的用法还是可以用的:

text = "Dear %(name)s, Give me $%(amount)0.2f"
print text % ( {"name":'Mrs. Liu', "amount":100.3} )

也可以用formart方法

text = "Dear {name}s, Give me {amount:0.2f}"
print text.format( name='Mrs. Liu', amount=100.3)

13、Generater和I/O
一般来说用生成器Generator与I/O操作结合,可以让内容的产生和I/O部分去耦合,另外附带的好处就是内存消耗更小(因为不是拼接好一堆string后再写入)

import sys
#产生内容
def content(n):
    while n > 0:
        yield "T-minus %d\n" % n
        n -= 1
    yield "HaHa"

#逐行写入,不费内存
ct = content(5)
f = sys.stdout
f.writelines(ct)

14、显然,Generator对buffer的利用不是最大化的,因此,有些时候我们也会采取拼接大字符串再一次性写入的方法。拼接可以用join:
(当然内存少不了。。)

......
"".join(lines)

15、Unicode字符串的处理。
绝对不要把unicode字符串和非unicode字符串连用!
在I/O操作时,会遇到很多的Unicode问题。解决方法有很多:
(1)通过encode和decode
s.decode(encoding,error) 将encoding编码的字符串转化为unicode编码字符串
s.encode(encoding,error) 将unicode编码的字符串转化为8-bit的、encoding格式的字节码
encoding可以取ascii latin-1(iso-8859-1) cp1252 utf-8 utf-16 utf-16-le utf-16-be unicode-escape
raw-unicode-escape等。
error是转化过程中的容错级别,默认是strict,可以选择ignore或者replace等。
(2)使用Unicode I/O
使用函数codes.open(filename,mode,encoding,error),后三个参数可选。
mode和open的mode类似。
encoding是指定read和write时的字符串编码

#这个例子要写入utf-8编码的字符串到文件
>>> import codecs
>>> str = u"计算所"
>>> f = codecs.open("test.txt",'w','utf-8')
>>> f.write(str)
>>> f.close()
#这个则是ascii编码(gbk)的
>>> import codecs
>>> str = "计算所"
>>> f = codecs.open("test.txt",'w')
>>> f.write(str)
>>> f.close()

16、如果已经打开了一个文件,并且想用codecs,则可以用codecs包装一下:
fenc = codecs.EncodedFile(f,”utf-8″)

17、Object序列化(持久化)。
Python中,对Object进行持久化非常容易,可以用pickle实现:
一个比较虎的地方貌似是支持循环引用。。比如下面的例子,我持久化o2,会把引用的o1也自动持久化了。。

#!/usr/bin/python

import pickle

class MyObj(object):
    def __init__(self,v,r):
        self.value = v
        self.ref = r

    def print123(self):
        print self.value
        if self.ref != None:
            print self.ref.value

if __name__ == "__main__":
    o1 = MyObj(1,None)
    o2 = MyObj(3,o1)

    print "Before store using pickle"
    o2.print123()

    f = open("obj.sav","wb")
    pickle.dump(o2,f)
    f.close()

    print "After load using pickle"
    f = open("obj.sav","rb")
    o = pickle.load(f)
    f.close()
    o.print123()

18、对象的持久化还可以直接使用shelve,他不用再open文件了。

import shelve
obj = SomeObject()
db = shelve.open("file")
db['key'] = obj
...
obj = db['key']
db.close()

19、实际上,shelve的底层使用了pickle模块,只不过将它写成文件时更易读懂。pickle的持久化格式随着版本有细微差异,可以用过dump(obj,file,protocol)的最后一个参数来解决。

20、如果希望自定义持久化的数据,可以来重写对象的__getstate__() 和 __setstate__()。它们会被pickle再dump和load的时候调用。比如对象涉及底层网络socket的时候就是一个例子。

import socket
class Client(object):
    def __init__(self,addr):
        self.server_addr = addr
        self.sock = socket.Socket(socket.AF_INET,socket.SOCK_STREAM)
        self.sock.connect(addr)
    def __getstate__(self):
        return self.server_addr
    def __setstate(self,value):
        self.server_addr = value
        self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.sock.connect(self.server_addr)

最后,这种序列化格式只是python内部使用,如果想和别的开发语言共用是不太可能的。

Leave a Reply

Your email address will not be published.