改善python的91个建议_库 -- 改善python程序的91个建议

In [58]: c = Counter("success")

In [59]: printc

Counter({'s': 3, 'c': 2, 'e': 1, 'u': 1})

In [60]: c.update("sucessfully") #累加而不是替换

In [61]: c

Out[61]: Counter({'s': 6, 'c': 3, 'u': 3, 'e': 2, 'l': 2, 'f': 1, 'y': 1})

In [63]: c.subtract("successfully")

In [64]: c

Out[64]: Counter({'s': 3, 'c': 1, 'e': 1, 'u': 1, 'f': 0, 'l': 0, 'y': 0})

建议40:深入掌握ConfigParser

配置文件的意义在于用户不需要修改代码,就可以改变应用程序的行为。 ConfigParser有几个地方需要提一下:

getboolean函数,根据一定的规则将配置项的值转换为布尔值。0,no,false和off都会被转义为False。1,yes,true,on都被转义为True。[section1] option1=0 当调用getboolean('section1','option1')时,返回的值是False。

配置项的查找规则。 有一个[DEFAULT]节,当读取的配置项不在指定的节里时,会在[DEFAULT]节中查找。

支持字符串格式化的类似语法

cat format.conf

[DEFAULT]

conn_str= %(dnb)s://%(user)s:%(pw)s@%(host)s:%(port)s/%s(db)s

dbn=mysql

user=root

host=localhost

port= 3306[db1]

user=aaa

pw=ppp

db=exmaple

[db2]

user=bbb

pw=ccc

db=exampleimportConfigParser

conf=ConfigParser.ConfigParser()

conf.read('foramt.conf')print conf.get('db1','conn_str')

以上配置可以根据不同配置获取不同数据库配置相应的连接字符串。

建议41:使用argparse处理命令行参数

Pythonista有好几种方案,标准库中留下来的getopt, optparse和argparse。 其中argparse是最强大的。

In [65]: import argparse

In [66]: parser = argparse.ArgumentParser()

In [67]: parser.add_argument('-o','--output')

Out[67]: _StoreAction(option_strings=['-o', '--output'], dest='output', nargs=No

ne, const=None, default=None, type=None, choices=None, help=None, metavar=None)

In [68]: parser.add_argument('-v',dest='verbose',action='store_true')

Out[68]: _StoreTrueAction(option_strings=['-v'], dest='verbose', nargs=0, const=

True, default=False, type=None, choices=None, help=None, metavar=None)

In [69]: args = parser.parse_args()

In [70]: args

Out[70]: Namespace(output=None, verbose=False)

现在又出现了docopt,比argparse更先进更易用的命令行参数处理器。(但是,还不是标准库的一部分)

建议42:使用pandas处理大型CSV文件

CSV(Comma Seperated Values)作为一种逗号分隔型值的纯文本格式文件,实际中经常用到。Python提供了处理csv的API:

1.reader( csvfile, dialect='excel', fmtparam ) 用于csv文件的读取,返回reader对象。当dialect设置为excel时,默认Dialect值如下:

class excel(Dialect):

delimiter = ',' # 单个字符,用于分隔字段,常见的有, | ;

quotechar = '"' # 用于对特殊符号加引号

doublequote = True # quotechar出现时候表现形式

skipinitialspace = False # true是delimiter后面的空格会忽略

lineterminator = '\r\n' #行结束符

quoting = QUOTE_MINIMAL # 是否在字段前加引号

2.csv.writer(csvfile, dialect=‘excel’, **fmtparams)用于写入CSV文件,参数同上。

3.csv.DictReader(csvfile, fieldnames=None, restkey=None, restval=None, dialect="excel", args,*kwds) 同reader方法类似,不同的是把信息映射到一个字典中去。

4.csv.DictWriter(csvfile, fieldnames, restval='', extrasaction='raise', dialect='excel', args,*kwds)用于支持字典写入。

CSV模块使用非常方便(import csv),但如果要处理的CSV文件大小上百MB或者几个GB,那么csv模块就应付不来了。 下面做一个实验,临时创建一个1GB的CSV文件并将其加载到内存中:

In [13]: f = open('large.csv','wb')

In [14]: f.seek(1073741824-1) # 创建大文件的技巧

In [15]: f.write("\0")

In [16]: f.close()

In [17]: import os

In [18]: os.stat("large.csv").st_size

Out[18]: 1073741824L

In [19]: with open("large.csv","rb") as csvfile:

....: mycsv = csv.reader(csvfile,delimiter=';')

....: for row in mycsv:

....: print row

....:

运行后会出现MemoryError。

所以应该使用pandas。其支持两种数据结构——Series和DataFrame是数据处理的基础。

Series:是一种类似数组的带索引的一维数据结构。通过obj.values()和obj.index()可以分别获取值和索引。

DataFrame:类似一个二维数据结构,支持行和列的索引。

pandas中处理csv文件的函数主要为read_csv()和to_csv(),前者读取csv文件内容并返回DataFrame,后者则相反。 ①可以指定读取部分列和文件的行数 ②设置CSV文件与excel兼容 ③对文件进行分块处理并返回一个可迭代的对象 ④当文件格式相似的时候,支持多个文件合并处理。

pandas处理非常灵活,而且底层使用Cython实现速度较快,在专业的数据处理与分析领域,如金融等行业已经得到广泛应用。

建议45:序列化的另一个不错的选择——JSON

Python有一系列模块提供对JSON格式的支持,如simplejson, cjson, yajl, ujson, 2.6后又引入了标准库JSON。cjson是用C语言实现,yajl是Cython版本的JSON实现。 simplejson与标准库JSON的区别不大,但更新可能更快,在实际使用中将这两者结合采用如下的import方法:

try:importsimplejson as jsonexceptImportError:import json

JSON的常用方法与pickle类似,dump/dumps序列化,load/loads反序列化。

相比pickle,json具有以下优势:

使用简单,支持多种数据类型。

名称/ 值对的集合

值的有序列表

存储格式可读性更为友好,容易修改。dumps函数提供了一个参数indent使生成的json文件可读性更好,0意味着每个值单独一行;大于0的数字表示使用这个数字的空格来缩进嵌套结构。但这个是以文件大小变大为代价的。

json支持跨平台跨语言操作,能够轻易被其他语言解析。

具有较强的扩展性。提供了编码(JSONEncoder)和解码类(JSONDecoder)以便用户对其默认不支持的序列化类型进行扩展。

但是Python中标准模块json的性能比pickle稍逊。性能要求高的话,还是选择cPickle。

dump/dumps用来序列化,load/loads用于反序列化

建议46:使用traceback获取栈信息

traceback会输出完整的栈信息,有利于开发人员快速找到异常发生时的现场信息。

tracback.print_exc()打印出:错误类型,错误对应的值以及具体的trace信息,包括文件名、具体行号、函数名以及对应的源代码。

常用方法:

tradeback.print_exception(type, value, traceback,[,limit[,file]]), 根据limit的设置打印站信息,file为None的情况下定位到sys.stderr,否则写入文件;其中type,value,traceback这3个参数对应的值可以从sys.exc_info()中获取

tradeback.print_exception([limit[,file]]),为print_exception的缩写。

traceback.format_exc([limit]) 返回字符串

traceback。extract_stack([file,[,limit]]) 从当前栈帧中提取trace信息

本质上type,value,traceback这3个参数对应的值可以从sys.exc_info()中获取。当有异常发生时,该函数以元祖的形式返回(type,value,traceback),其中type为异常类型,value为异常本身,traceback为异常发生时的调用和堆栈信息,它是一个traceback对象,包括出错函数、位置等数据

inspect模块也提供了获取traceback对象的接口,inspect.trace([context])可以返回当前帧对象及异常发生时进行捕获的帧对象之间的所有栈帧记录,因此第一个记录代表当前调用记录,最后一个代表异常发生时候的对象,其中每一个列表都是由6个元素组成的元组(frame对象,文件名,当前行号,函数名,源代码列表,当前行在源代码列表中的位置)。还可以通过inspect模块的inspect.stack()函数查看函数层级调用的栈相关信息。

importtraceback

gList= ['a','b','c','d','f','g','e']deff():

gList[5]returng()defg():returnh()defh():del gList[2]returni()defi():

gList.append('i')print gList[7]if __name__ == '__main__':try:

f()exceptIndexError as ex:print 'sorry, out of range'

printex

traceback.print_exc()

输出结果:

sorry, out of range

list index out of range

Traceback (most recent call last):

File"trace_back.py", line 17, in f()

File"trace_back.py", line 5, infreturng()

File"trace_back.py", line 7, ingreturnh()

File"trace_back.py", line 10, inhreturni()

File"trace_back.py", line 13, iniprint gList[7]

IndexError: list index out of range

建议47:使用logging记录日志信息

logger分为5个级别, 可以通过Logger.setLevel(lvl)设置,分别为debug,info,warning,error,critical,其中默认为warning

包含4个主要对象:

logger:程序信息输出的接口,分散在不同代码中,使得程序可以在运行的时候记录相应的信息,并根据设置的日志级别或者filter来决定哪些信息需要输出,并将这些信息分发到其关联的handler。

handler。处理信息的输出,可以输出到控制台、文件或者网络。可以通过Logger.addHandler()来给logger对象添加handler,常用的handler有StreamHandler和FileHandler类。StreamHandler发送错误信息到流,FileHandler类用于向文件输出日志信息,这两个handler在logging的核心模块中。其他的handler定义在logging.handlers模块中。

Formatter。决定log信息的格式。

Filter。决定哪些信息需要输出。可以被handler和logger使用,支持层级关系。比如,如果设置了filter名称为A.B的logger,则该logger和其子logger的信息会被输出,如A.B,A.B.C

logging.basicConfig([**kwargs])) 提供了对日志系统的基本配置,默认使用StreamHandler和F偶然matter并添加到root logger,支持的字段参数:

FormatDescription

filename

Specifies that a FileHandler be created, using the specified filename, rather than a StreamHandler.

filemode

Specifies the mode to open the file, if filename is specified (if filemode is unspecified, it defaults to ‘a’).

format

Use the specified format string for the handler.

datefmt

Use the specified date/time format.

level

Set the root logger level to the specified level.

stream

Use the specified stream to initialize the StreamHandler. Note that this argument is incompatible with ‘filename’ - if both are present, ‘stream’ is ignored.

为了使logging使用更为简单,logging支持logging。config进行设置,支持dictConfig和fileConfig两种形式,其中fileConfig是基于configparser()函数进行解析,必须包含的内容为[loggers], [handlers]和[formatters]。具体实例:

关于logging的使用建议:

尽量为logging去一个名字而不是采取默认,这样当在不同的模块中使用的时候,其他模块值需要使用以下代码就可以方便地使用同一个logger,因为它本质上符合单例模式。

importlogging

logging.basicConfig(level=logging.DEBUG)

logger=logging.getLogger(__name__)

为了方便找到问题,logging的名字建议以模块或者class来命名。logging名称遵循'.'换分的继承规则。根是root logger,logger a.b的父logger对象是a

Logging只是线程安全的,不支持多进程写入同一个日志文件,因此对于多个进程,需要配置不同的日志文件。

建议48:使用threading模块编写多线程程序

GIL(Global Interpreter Lock)全局解释器锁,是解释器用于同步线程的工具,使得任何时候只有一个线程在运行。GIL的存在使得Python多线程编程暂时无法利用多处理器的优势。但这不意味着我们要放弃多线程。 对于纯Python的代码也许使用多线程不能提高运行效率,但是以下几种情况,多线程仍然是比较好的解决方案:

等待外部资源返回

建立反应灵活的用户界面

多用户应用程序

Python为多线程编程提供了两个模块:

thread

threading

thread模块提供了多线程的底层支持模块,以低级方式来处理和控制线程,使用起来较为复杂; threading模块基于thread进行封装,将线程操作对象化,在语言层面提供了丰富的特性。 因此,实际使用中推荐优先使用threading模块,因为:

threading对同步原语的支持更为完善和丰富。如有Lock指令锁,还支持条件变量condition、信号量Semaphore等。

threading模块在主线程与子线程的交互上更为友好。threading中的join()方法能够阻塞当前上下文环境的线程,直到调用此方法线程终止或者到达指定的timeout。利用该方法可以方便地控制主线程与子线程以及子线程之间的执行。

importthreading, time, sysclasstest(threading.Thread):def __init__(self, name, delay):

threading.Thread.__init__(self)

self.name=name

self.delay=delaydefrun(self):print '%s delay for %s' %(self.name, self.delay)

time.sleep(self.delay)

c=0while 1:print 'This is thread %s on line %s' %(self.name, c)

c= c + 1

if c == 3:print 'End of thread %s' %self.namebreakt1= test('Thread 1', 2)

t2= test('Thread 2', 2)

t1.start()print 'Wait t1 to end't1.join()

t2.start()print 'End of main'

运行结果:

threading_join.png

主线程main上使用t1的join()方法,主线程会等待t1结束后才继续运行后面的语句。线程t2的启动在join语句之后,t2一直等到t1退出后才会开始运行。

thread模块不支持守护进程。thread模块在主线程退出后,所有的主线程无论是否还在工作,都会被强制结束。threading模块,支持守护进程,可以通过setDaemon()来设定线程的daemon属性,当daemon属性为True时,表面主线程的退出可以不用等待子线程的完成(默认为False,即所有子线程结束后主线程才会结束)

from thread importstart_new_threadimporttimedefmyfunc(a, delay):print 'I will calculate square of %s after delay for %s' %(a,delay)

time.sleep(delay)print 'calculate begins...'result= a*aprintresultreturnresult

start_new_thread(myfunc, (2,5))

start_new_thread(myfunc, (6,8))

time.sleep(1)

I will calculate square of2 after delay for 5I will calculate square of 6 after delay for 8

importthreadingimporttimedefmyfunc(a, delay):print 'I will calculate square of %s after delay for %s' %(a,delay)

time.sleep(delay)print 'calculate begins...'result= a*aprintresultreturnresult

t1= threading.Thread(target=myfunc, args=(2,5))

t2= threading.Thread(target=myfunc, args=(6,8))printt1.isDaemon()printt2.isDaemon()

t2.setDaemon(True)

t1.start()

t2.start()

运行结果:

threading.png

建议49:使用Queue使多线程编程更安全

线程间的同步互斥,线程间数据的共享等这些都是涉及线程安全要考虑的问题。

Python中的Queue提供了三种队列:

Queue.Queue(maxsize)。FIFO

Queue.LifoQueue(maxsize)

Queue.PriorityQueue(maxsize)。优先级队列

这三种队列支持以下几种方法:

Queue.qsize()

empty()

full()

put(item, block, timeout ):往队列中添加item元素,block为False的时候,若队列满则抛出Full异常,若block为True,则队列满一直等待有空位置,直到timeout时间后抛出异常。

put_nowait(item): 相当于block为False的put方法

get(block, timeout ): 与put类似的用法

get_nowait()

task_done():发送信号表明入列任务已经完成,经常在消费者线程中使用

join():阻塞直到队列中所有元素处理完毕

queue本身是线程安全的。

Queue实现了多个生产者和多个消费者的队列,当多线程之间需要信息安全交换的时候特别有用。 需要注意的是Queue与collections.deque中的队列是不同的,前者主要用于不同线程之间的通信,它内部实现了线程的锁机制;而后者是数据结构上的概念。

例子:

importosimportQueueimportthreadingimporturllib2classDownloadThread(threading.Thread):def __init__(self, queue):

threading.Thread.__init__(self)

self.queue=queuedefrun(self):whileTrue:

url=self.queue.get()print self.name + "begin download" + url + "..."self.download_file(url)

self.queue.task_done()print self.name + "download completed!"

defdownload_file(self,url):

urlhandler=urllib2.urlopen(url)

fname= os.path.basename(url)+".html"with open(fname,"wb") as f:whileTrue:

chunk= urlhandler.read(1024)if not chunk: breakf.write(chunk)if __name__ == '__main__':

urls= ["http://www.baidu.com","http://www.google.com","http://www.wengweitao.com"]

queue=Queue.Queue()for i in range(5):

t=DownloadThread(queue)

t.setDaemon(True)

t.start()for url inurls:

queue.put(url)#wait for the queue to finish

queue.join()

结果:

threading_queue.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值