原文發表在我的 "博客主頁" ,轉載請註明出處! 建議三十四:掌握字元串的基本用法 編程有兩件事,一件是處理數值,另一件是處理字元串,在商業應用編程來說,處理字元串的代碼超過八成,所以需要重點掌握。 首先有個小技巧,python遇到未閉合的小括弧時會自動將多行代碼拼接為一行,同時把相鄰的兩個字...
原文發表在我的博客主頁,轉載請註明出處!
建議三十四:掌握字元串的基本用法
編程有兩件事,一件是處理數值,另一件是處理字元串,在商業應用編程來說,處理字元串的代碼超過八成,所以需要重點掌握。
首先有個小技巧,python遇到未閉合的小括弧時會自動將多行代碼拼接為一行,同時把相鄰的兩個字元串字面量拼接再議,比如下麵的兩個代碼是等價的。
s = ('SELECT *'
'FROM atable'
'WHERE afield = "value"')
'SELECT *FROM atableWHERE afield = "value"'
除此之外,python的字元串有str和unicode兩種,判斷一個變數s是不是字元串應使用isinstance(s, basestring)。
#input
a = "hi"
b = u"Hi"
print isinstance(b, str)
print isinstance(b, basestring)
print isinstance(b, unicode)
print isinstance(a, unicode)
#output
False
True
True
False
通過上面的例子可以總結:
- isinstance(a, str)用於判斷一個字元串是不是普通字元串,也就是說其類型是否為str
- isinstance(a, unicode)用來判斷一個字元串是不是Unicode
- 因此判斷一個變數是不是字元串,應該使用isinstance(a, basestring),basestring是str和Unicode的基類
下麵簡單來列舉下字元串的使用 - 性質判定
str對象有以下幾個方法:isalnum(), isalpha(), isdigit(), islower(), isupper(), isspace(), istitle(), startwith(prefix[,start[,end]]),endwith(suffix[,start[,end]]) - 查找替換
有以下函數:count(sub[,start[,end]]),find(sub[,start[,end]]), index(sub[,start[,end]]), rfind(sub[,start[,end]]), rindex(sub[,start[,end]]),replace(old,new[,count]),其中find()函數族找不到時返回-1,index()函數族則拋出異常。 - 分切與連接
主要掌握兩個:partition(sep), split([sep[,maxsplit]]),前者接受一個字元串參數,並返回一個3個元素的元組對象,如果sep沒有出現,返回值是(sep,","),否則返回值的第一個元素是sep左邊的部分,第二個元素是sep本身,第三個元素是sep右端的部分。而split()的參數maxsplit是分切的次數,即最大的分切次數,所以返回值做多有maxsplit+1個元素。 - 變形
主要是一些常用函數,lower(),upper(),capitalize(),swapcase(),title() - 填充與刪除
如果strip([chars]),lstrip([chars]),rstrip([chars])中的chars參數沒有指定,就是刪除空白符。填充則用於字元串的輸出,藉助於它們能夠排出漂亮的版面。center(width[,fillchar]),ljust(width[,fillchar]),rjust(width[,fillchar]),zfill(width),expandtabs([tabsize])這些完成的居中,左對齊,右對齊,零填充等功能。
建議三十五:按需選擇sort()或者sorted()
兩者的函數形式如下:
sorted(iterable[,cmp[,key[,reverse]]])
s.sort([cmp[,key[,reverse]]])
#cmp為用戶定義的任何比較函數,函數的參數為兩個可比較的元素(來自iterable或者s),函數根據第一個和第二個參數的關係返回-1,0,1,預設值為None
#key是帶一個參數的函數,用來為每個元素提取比較值,預設為None(即直接比較每個元素)
#reverse表示排序結果是否反轉
#exampe
p = [{'name':'Jon','age':32},{'name':'Alan','age':50},{'name':'Jon','age':23}]
print sorted(p,key=lambda x: (x['name'], -x['age']))
兩者區別如下:
- sorted()用於任意可迭代對象,而sort()一般作用於列表
- 當排序對象為列表的時候兩者適用的場景不同,sorted()會返回一個排序後的列表,原有列表保持不變,而sort()函數會直接修改原有列表,函數返回None
- 不論用哪個,傳入參數key都要比傳入參數cmp效率高,cmp傳入的函數在整個排序過程中會調用多次,函數開銷較大,而key針對每個元素僅作一次處理。
建議三十六:使用copy模塊深拷貝對象
淺拷貝:構造一個新的複合對象並將從原對象中發現的引用插入該對象中。淺拷貝的實現方式主要有:工廠函數,切片操作,copy模塊中的copy操作
深拷貝:構造一個新的複合對象,遇到引用會繼續遞歸拷貝其所指向的具體內容,它會針對引用所指向的對象繼續執行拷貝,產生的對象不受其他引用對象操作的影響。
在包含引用的數據結構中,淺拷貝並不能進行徹底的拷貝,當存在列表、字典等對象的時候,它僅僅拷貝其引用地址,修改拷貝的對象可能會改變原對象的值,要解決這個問題需要用到深拷貝,深拷貝不僅拷貝引用也拷貝引用所指向的對象,深拷貝得到的對象和原對象是相互獨立的。使用如下:
import copy
copy.deepcopy(x)
建議三十七:使用Counter進行計數統計
計數統計具有非常廣泛的用途,逐一來看實現方式:
使用dict
data = ['a','2',2,3,4,'2','b','a','b',3,4,'a'] count_frq = {} for item in data: if item in count_frq: count_frq[item] += 1 else: count_frq[item] = 1 print count_frq
使用defaultdict
from collections import defaultdict data = ['a','2',2,3,4,'2','b','a','b',3,4,'a'] count_frq = defaultdict(int) for item in data: count_frq[item] += 1 print count_frq
使用set和list
data = ['a','2',2,3,4,'2','b','a','b',3,4,'a'] count_set = set(data) count_frq = [] for item in count_set: count_frq.append((item,data.count(item))) print count_frq
上面的方法都比較naive,更加pythonic的如下:
from collections import Counter data = ['a','2',2,3,4,'2','b','a','b',3,4,'a'] print Counter(data)
Counter屬於字典類的子類,是一個容器對象,主要用來統計散列對象,支持集合操作,提供了三種初始化方式:
Counter('success') Counter(s=3,c=2,e=1,u=1) Counter('s':3,'c':2,'u':1,'e':1)
可以使用elements()方法來獲取Counter中的key值,利用most_common(N)方法獲取N個出現頻率最高的元素以及對應的次數,當訪問元素不存在時候,預設返回0。
from collections import Counter data = ['a','2',2,3,4,'2','b','a','b',3,4,'a'] print list(Counter(data).elements()) print Counter(data).most_common(3) print Counter(data)['z']
建議三十八:深入掌握ConfigParser
幾乎所有的應用程式真正運行起來的時候,都會讀取一個或幾個配置文件,配置文件的意義在於用戶不需要修改代碼,就可以改變應用程式的行為。常見的配置文件格式有XML和ini等。這裡強調幾點用法:
getboolean()函數,其根據一定的規則將配置項的值轉化為布爾值,如:
當調用getboolean('section1','option1')時,將返回False。不過他的真值規則值得一說:處了0以外,no, false, off都會轉義為False,對應的1, yes, true, on都會轉義為True,其他值會拋出異常。[section1] option1 = 0
配置項的查找規則
首先,在ConfigParser支持的配置文件格式里,有一個[DEFAULT]節,當讀取的配置項不在指定的節里時,ConfigParser將會到[DEFAULT]節中查找。#cyj.conf [DEFAULT] in_default = 'if you have a dream, you have to protect it' [section1] #test.py import ConfigParser conf = ConfigParser.ConfigParser() conf.read('cyj.conf') print conf.get('section1','in_default') #output 'if you have a dream, you have to protect it'
除此之外,還有一些機制導致項目配置的查找更複雜,這就是class ConfigParser構造函數中的defaults形參以及其get(section,option[,raw[,vars]])中的全名參數vars。如果把這些機制全部用上,查找規則如下:
- 如果找不到節名,就拋出NoSectionError
- 如果給定的配置項出現在get()方法的vars參數中,則返回vars參數中的值
- 如果在指定的節中含有給定的配置項,則返回值
- 如果在[DEFAULT]中含有指定的配置項,返回值
- 如果在構造函數的defaults參數中有指定的配置項,則返回值
- 拋出NoOptionError
ConfigParser其他配置方法
python字元串的格式化可以使用如下語法:site = {'protocol':'http','server':'115.159.48.140','port':80} print '%(protocol)s://%(server)s:%(port)s/' %site
ConfigParser支持類似的用法,所以在配置文件中可以使用,比如:
#cyj.conf [DEFAULT] conn_str = %(dbn)s://%(user)s:%(pw)s@%(host)s:%(port)s/%(db)s dbn = mysql user = root host = localhost port = 3306 [db1] user = aaa pw = ppp db = example [db2] host = 192.168.0.110 pw = www db = example #test.py import ConfigParser conf = ConfigParser.ConfigParser() conf.read('cyj.conf') print conf.get('db1','conn_str') print conf.get('db2','conn_str') #output mysql://aaa:ppp@localhost:3306/example mysql://root:[email protected]:3306/example
建議三十九:使用argparse處理命令行參數
儘管應用程式通常能夠通過配置文件在不修改代碼的情況下改變行為,但提供靈活易用的命令行參數依然非常有意義,比如:減輕用戶的學習成本,通常命令行參數的用法只需要在應用程式名後加--help參數就能獲得。
現在比較流行的庫為argparse*庫,他子optparse脫胎而來,先生成一個parser實例,然後增加參數聲明。比如:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-o','--output')
parser.add_argument('-v',dest='verbose',action='store_true')
args = parser.parse_args()
add_argument()方法用以增加一個參數聲明,他支持的類型比較多,語法直觀,type參數的值不再是一個字元串,而是一個可調用對象。此外,add_argument()提供了對必填參數的支持,只要把required參數設置為True傳遞進去,當缺失這一參數時,argparser就會自動退出程式,並提示用戶。
ArgumentParser還支持參數分組,add_argument_group()可以在輸出幫助信息時更加清晰。
test.py
import argparse
parser = argparse.ArgumentParser(prog='PROG',add_help=False)
group1 = parser.add_argument_group('group1','python')
group1.add_argument('foo',help='foo help')
group2 = parser.add_argument_group('group2','nice')
group2.add_argument('--bar',help='bar help')
print parser.print_help()
建議四十:使用pandas處理大型CSV文件
CSV(Comma Separated Values)作為一種逗號分隔型值的純文本文件,在實際應用中經常用到,如資料庫數據的導入導出,數據分析中記錄的存儲等。首先來看下python提供的CSV處理相關的API:
- csv.reader(csvfile[,dialect='excel'][,fmtparam]),用於CSV文件的讀取,返回一個reader對象用於在CSV文件內容上進行行迭代
csv.writer(csvfile,dialect='excel',**fmtparams)用於寫入CSV文件
import csv with open('data.csv','wb') as csvfile: csvwriter = csv.writer(csvfile, dialect='excel', delimiter='|', quotechar='"', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["1/3/09 14:44","'Product1'","1200''","Visa","Gouya"])
- csv.DictReader(csvfile, fieldnames=None, restkey=None, restval=None, dialect='excel',*args,**kwargs),和reader()類似,不同的是將讀入的信息映射到一個字典中去,其中字典的key由fieldnames指定,省略的話CSV文件第一行的數據作為key值。如果讀入行的欄位的個數大於fieldnames中指定的個數,多餘的欄位名將會存放在restkey中,而restval主要用於當讀取行的域的個數小於fieldnames的時候,它的值將會被用作剩下的key對應的值
csv.DictWriter(csvfile, fieldnames, restval='',extrasaction='raise',dialect='excel',*args,**kwds)用於支持字典寫入
上面的使用可以滿足大部分需求,但是如果處理大文件會拋出MemoryError異常,這種情況下可以使用pandas模塊。
Pandas,支持多種文件格式處理,包括CSV,HDF5,HTML等,兩種數據結構:Series和DataFrame。
Series:是一種類似數組的帶索引的一維數據結構,支持的類型與NumPy相容,如果不指定索引,預設從0到N-1,通過obj.values()和obj.index()分別獲取值和索引,當給Series傳遞一個字典的時候,Series的索引將根據字典中的鍵排序,如果傳入字典的時候重新制訂了index參數,當index與字典中的鍵不匹配的時候,會出現數據丟失的情況,標記為NaN。
DataFrame:類似於電子錶格,其數據結構為排好序的數據列的集合,每一列都可以是不同的數據類型,類似於二維數組,支持行和列的索引。可以通過colums指定序列的順序。import pandas data = {'OrderDate': ['1-6-10', '1-23-10', '2-9-10', '2-26-10', '3-15-10'], 'Region': ['East', 'Central', 'Central', 'West', 'East'], 'Rep': ['Jones', 'Kivell', 'Jardine', 'Gill', 'Sorvino']} print pandas.DataFrame(data,columns=['OrderDate','Rep','Region'])
pandas中處理CSV文件的主要函數為read_csv()和to_csv(),前者讀取文件的內容返回DataFrame,後者為其逆過程。下麵簡單講述其用法:
指定讀取部分列和文件的行數
df = pd.read_csv('data.csv',nrows=5,usecols=[]) #nrows指定讀取文件的行數 #usecols指定索要讀取的列的列名,沒有列名可用索引
- 設置CSV文件和excel相容
如果文件格式改為使用“|”分隔符,則需要設置dialect相關的參數,error_bad_lines設置為False,當記錄不符合要求的時候,可直接忽略。 對文件進行分塊處理並返回一個可迭代的對象
為了防止記憶體消耗過大,可以分批載入記憶體,參數chunksize設置分塊的文件行數,將參數iterator設置為True時,返回值為TextFileReader,是一個可迭代對象reader = pd.read_table("data.csv",chunksize=10,iterator=True) iter(reader).next()
當文件格式相似的時候,支持多個文件合併處理
dfs = [pd.read_csv(f) for f in fileset] totol_fg = pd.concat(dfs)
參考:編寫高質量代碼--改善python程式的91個建議