程式的主要功能
現在有個儲存使用者資訊的像表格一樣的文件:第一行是屬性,各個屬性用逗號(,)分隔,從第二行開始每行是各個屬性對應的值,每行代表一個使用者。如何實現讀入這個文件,每行輸出一個使用者物件呢?
另外還有4個小要求:
- 每個文件都很大,如果一次性把所有行生成的那麼多物件存成列表返回,記憶體會崩潰。程式中每次只能存一個行生成的物件。
- 用逗號隔開的每個字串,前後可能有雙引號(”)或者單引號(’),例如”張三“,要把引號去掉;如果是數字,有+000000001.24這樣的,要把前面的+和0都去掉,提取出1.24
- 文件中有時間,形式可能是2013-10-29,也可能是2013/10/29 2:23:56 這樣的形式,要把這樣的字串轉成時間型別
- 這樣的文件有好多個,每個的屬性都不一樣,例如這個是使用者的資訊,那個是通話紀錄。所以類中的具體屬性有哪些要根據文件的第一行動態生成
下面一一進行解決
實現過程
1.類的定義
由於屬性是動態新增的,屬性-值 對也是動態新增的,類中要含有updateAttributes()和updatePairs()兩個成員函式即可,此外用列表attributes儲存屬性,詞典attrilist儲存對映。其中init()函式為建構函式。__attributes前有下劃線表示私有變數,不能在外面直接呼叫。例項化時只需a=UserInfo()即可,無需任何引數。
1 2 3 4 5 6 7 8 9 10 |
class UserInfo(object): 'Class to restore UserInformation' def __init__ (self): self.attrilist={} self.__attributes=[] def updateAttributes(self,attributes): self.__attributes=attributes def updatePairs(self,values): for i in range(len(values)): self.attrilist[self.__attributes[i]]=values[i] |
2.用生成器(generator)動態更新每個物件並返回物件
生成器相當於一個只需要初始化一次,就可自動執行多次的函式,每次迴圈返回一個結果。不過函式用return 返回結果,而生成器用yield 返回結果。每次執行都在yield返回,下一次執行從yield之後開始。例如,我們實現斐波拉契數列,分別用函式和生成器實現:
1 2 3 4 5 6 7 |
def fib(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b n = n + 1 return 'done' |
我們計算數列的前6個數:
1 2 3 4 5 6 7 8 |
>>> fib(6) 1 1 2 3 5 8 'done' |
如果用生成器的話,只要把 print 改成 yield 就可以了。如下:
1 2 3 4 5 6 |
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 |
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
>>> f = fib(6) >>> f <generator object fib at 0x104feaaa0> >>> for i in f: ... print(i) ... 1 1 2 3 5 8 >>> |
可以看到,生成器fib本身是個物件,每次執行到yield會中斷返回一個結果,下次又繼續從yield的下一行程式碼繼續執行。生成器還可以用generator.next()執行,更多生成器的講解請見URL
在我的程式中,生成器部分程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
def ObjectGenerator(maxlinenum): filename='/home/thinkit/Documents/usr_info/USER.csv' attributes=[] linenum=1 a=UserInfo() file=open(filename) while linenum 'gb2312')#linecache.getline(filename, linenum,'gb2312') if line=='': print'reading fail! Please check filename!' break str_list=line.split(',') for item in str_list: item=item.strip() item=item.strip('\"') item=item.strip('\'') item=item.strip('+0*') item=catchTime(item) if linenum==1: attributes.append(item) else: values.append(item) if linenum==1: a.updateAttributes(attributes) else: a.updatePairs(values) yield a.attrilist #change to ' a ' to use linenum = linenum +1 |
其中,a=UserInfo()為類UserInfo的例項化.因為文件是gb2312編碼的,上面使用了對應的解碼方法。由於第一行是屬性,有個函式將屬性列表存入UserInfo中,即updateAttributes();後面的行則要將 屬性-值 對讀入一個字典中儲存。p.s.python中的字典相當於對映(map).
3.使用strip 去除不必要的字元
從上面程式碼中,可以看到使用str.strip(somechar)即可去除str前後的somechar字元。somechar可以是符號,也可以是正規表示式,如上:
1 2 3 4 |
item=item.strip()#除去字串前後的所有轉義字元,如\t,\n等 item=item.strip('\"')#除去前後的" item=item.strip('\'') item=item.strip('+0*')#除去前後的+00...00,*表示0的個數可以任意多,也可以沒有 |
4.re.match匹配字串
函式語法:
re.match(pattern, string, flags=0)
函式引數說明:
引數 描述
pattern 匹配的正規表示式
string 要匹配的字串。
flags 標誌位,用於控制正規表示式的匹配方式,如:是否區分大小寫,多行匹配等等。
若匹配成功re.match方法返回一個匹配的物件,否則返回None。`
1 2 3 4 |
>>> s='2015-09-18' >>> matchObj=re.match(r'\d{4}-\d{2}-\d{2}',s, flags= 0) >>> print matchObj <_sre.SRE_Match object at 0x7f3525480f38> |
5.使用time.strptime提取字串轉化為時間物件
在time模組中,time.strptime(str,format)可以把str按照format格式轉化為時間物件,format中的常用格式有:
- %y 兩位數的年份表示(00-99)
- %Y 四位數的年份表示(000-9999)
- %m 月份(01-12)
- %d 月內中的一天(0-31)
- %H 24小時制小時數(0-23)
- %I 12小時制小時數(01-12)
- %M 分鐘數(00=59)
- %S 秒(00-59)
此外,還需要使用re模組,用正規表示式,對字串進行匹配,看是否是一般時間的格式,如YYYY/MM/DD H:M:S, YYYY-MM-DD等
在上面的程式碼中,函式catchTime就是判斷item是否為時間物件,是的話轉化為時間物件。程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import time import re def catchTime(item): # check if it's time matchObj=re.match(r'\d{4}-\d{2}-\d{2}',item, flags= 0) if matchObj!= None : item =time.strptime(item,'%Y-%m-%d') #print "returned time: %s " %item return item else: matchObj=re.match(r'\d{4}/\d{2}/\d{2}\s\d+:\d+:\d+',item,flags=0 ) if matchObj!= None : item =time.strptime(item,'%Y/%m/%d %H:%M:%S') #print "returned time: %s " %item return item |
完整程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
import collections import time import re class UserInfo(object): 'Class to restore UserInformation' def __init__ (self): self.attrilist=collections.OrderedDict()# ordered self.__attributes=[] def updateAttributes(self,attributes): self.__attributes=attributes def updatePairs(self,values): for i in range(len(values)): self.attrilist[self.__attributes[i]]=values[i] def catchTime(item): # check if it's time matchObj=re.match(r'\d{4}-\d{2}-\d{2}',item, flags= 0) if matchObj!= None : item =time.strptime(item,'%Y-%m-%d') #print "returned time: %s " %item return item else: matchObj=re.match(r'\d{4}/\d{2}/\d{2}\s\d+:\d+:\d+',item,flags=0 ) if matchObj!= None : item =time.strptime(item,'%Y/%m/%d %H:%M:%S') #print "returned time: %s " %item return item def ObjectGenerator(maxlinenum): filename='/home/thinkit/Documents/usr_info/USER.csv' attributes=[] linenum=1 a=UserInfo() file=open(filename) while linenum 'gb2312')#linecache.getline(filename, linenum,'gb2312') if line=='': print'reading fail! Please check filename!' break str_list=line.split(',') for item in str_list: item=item.strip() item=item.strip('\"') item=item.strip('\'') item=item.strip('+0*') item=catchTime(item) if linenum==1: attributes.append(item) else: values.append(item) if linenum==1: a.updateAttributes(attributes) else: a.updatePairs(values) yield a.attrilist #change to ' a ' to use linenum = linenum +1 if __name__ == '__main__': for n in ObjectGenerator(10): print n #輸出字典,看是否正確 |