原文发表在我的博客主页,转载请注明出处!

建议四十一:一般情况下使用ElementTree解析XML

python中解析XML文件最广为人知的两个模块是xml.dom.minidomxml.sax,作为主要解析XML方法的两种实现,DOM需要将整个XML文件加载到内存中并解析为一棵树,简单但是内存消耗大;SAX是基于事件驱动的,虽不需要全部装入XML文件,但是处理过程复杂。一般情况下选择ElementTree便可以,cElementTree是其Cython实现,速度更快,消耗内存更少,性能上更好。使用ElementTree的特性有:

  • 使用简单,它将整个XML文件以树的形式展示,每一个元素的属性以字典的形式表示,非常方便处理
  • 内存上消耗明显低于DOM解析,在底层进行了一定的优化,解析工具支持SAX事件驱动
  • 支持 XPath 查询,非常方便获取任意节点的值

建议四十二:理解模块pickle优劣

python中有很多支持序列化的模块,像pickle,json等

序列化,就是把内存中的数据结构在不丢失其身份和类型信息的情况下转成对象的文本或二进制表示的过程,比如在磁盘上保存当前程序的状态数据以便重启的时候能够重新加载,多用户或者分布式系统中数据结构的网络传输时,可以将数据序列化后发送给一个可信网络对端,接收者进行反序列化后便可以重新恢复相同的对象等

pickle是最通用的序列化模块了,他主要有两个函数dump()load(),分别用来进行对象的序列化和反序列化,函数定义如下:

  • pickle.dump(obj, file[, protocol]):序列化数据到一个文件描述符(一个打开的文件、套接字等)。参数obj表示需要序列化的对象,包括布尔、数字、字符串、字节数组、None、列表、元祖、字典和集合等基本数据类型。参数file支持write()方法的文件句柄,可以为真实的文件,也可以是StringIO对象等
  • pickle.load(file):表示把文件中的对象恢复为原来的对象
  1. import cPickle as pickle
  2. my_data = {"name" : "Python", "type" : "Language", "version" : "2.7.5"}
  3. fp = open("picklefile.dat","wb")
  4. pickle.dump(my_data, fp)
  5. fp.close()
  6. fp = open("picklefile.dat","rb")
  7. out = pickle.load(fp)
  8. fp.close()
  9. print out
  10. print type(out)

pickle拥有良好的特性:

  • 接口简单,容易使用
  • pickle的存储格式具有通用性,能够被不同平台的python解析器共享
  • 支持的数据类型广泛
  • pickle模块是可以扩展的
  • 能够自动维护对象间的引用,如果一个对象上存在多个引用,pickle后不会改变对象间的引用,并且能够自动处理循环和递归引用
  1. import cpickle as pickle
  2. a = [1, 2]
  3. b = a
  4. b.append(3)
  5. p = pickle.dumps((a, b))
  6. a1, b1 = pickle.loads(p)
  7. print a1, b1
  8. a1.append(4)
  9. print b1

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

JSON(JavaScript Object Notation)是一种轻量级数据交换格式。相对于上文提到的pickle,JSON有如下优势:

  • 使用简单,支持多种数据类型,JSON文档的构成非常简单,仅存在两大数据结构
  • 名称/值对的集合
  • 值的有序列表
  • 存储格式可读性更为友好,容易修改
  • JSON支持跨平台跨语言操作,能够轻易被其他语言解析,而pickle只能在python语言中使用,另外相比于pickle,JSON的存储格式更为紧凑,所占空间更小
  • 具有较强的扩展性,JSON模块还提供了编码和解码类,以便用户对其默认不支持的序列化类型进行扩展

建议四十四:使用traceback获取栈信息

首先来看一个简单的例子:

  1. gList = ['a','b','c','d','e','f','g']
  2. def f():
  3. gList[5]
  4. return g()
  5. def g():
  6. return h()
  7. def h():
  8. del gList[2]
  9. return i()
  10. def i():
  11. gList.append('i')
  12. print gList[7]
  13. if __name__ == '__main__':
  14. try:
  15. f()
  16. except IndexError as ex:
  17. print "Sorry,Exception occured,you accessed an element out of range"
  18. print ex

这个例子比较简单,开发人员也为自己和用户打印出了错误信息,但是如果要debug,怎么才能快速地知道错误发生在哪里呢?traceback模块可以满足这个需求,它会输出完整的栈信息,将上面的代码修改下:

  1. except IndexError as ex:
  2. print "Sorry,Exception occured,you accessed an element out of range"
  3. print ex
  4. traceback.print_exc()

再次运行,程序会输出发生异常时候完整的栈信息,包括调用顺序、异常发生的语句、错误类型等。

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


建议四十五:使用logging记录日志信息

logging模块提供了日志功能,将logger的level分为5个级别,如下图,可以通过Logger.setLevel(lvl)来设置,默认的为WARNING



logging lib包含了以下4个主要对象:

  • logger logger是程序信息输出的接口,分散在不同的代码中,使得程序可以在运行的时候记录相应的信息,根据设置的日志级别或filter来决定哪些信息需要输出,并将这些信息分发到其关联的handler。
  • Handler 用来处理信息的输出,可以将信息输出到控制台、文件或者网络。
  • Formatter 决定log信息的格式,格式类似于“%()s”
  • Filter 决定哪些信息需要输出

关于logging的使用:

  • 尽量为logging取一个名字而不是采用默认,这样挡在不同的模块中使用的时候,其他模块只需要使用一下代码就可以方便地使用同一个logger。
  1. import logging
  2. logging.basicConfig(level = logging.DEBUG)
  3. logger = logging.getLogger(__name__)
  • 为了方便地找出问题所在,logging的名字建议以模块或者class来命名
  • logging是线程安全的,不支持多进程写入同一个文件

建议四十六:使用threading模块编写多线程程序

GIL使得python多线程编程暂时无法充分利用多处理器的优势,对于只含纯python的代码也许并不能提高运行效率,但是在以下情况中,比如等待外部资源返回,为了提高用户体验建立反应灵活的用户界面还是可以使用的。

python提供了thread和threading两个关于多线程的模块:

  • thread模块提供了多线程底层支持模块,以低级原始的方式来处理和控制线程,使用复杂
  • threading模块基于thread进行包装,将线程的操作对象化,在语言层面提供了丰富的特性
  • threading模块对同步原语的支持更为完善和丰富
  • threading模块在主线程和子线程交互上更为友好,看一个例子:
  1. import threading, time,sys
  2. class test(threading.Thread):
  3. def __init__(self,name,delay):
  4. threading.Thread.__init__(self)
  5. self.name = name
  6. self.delay = delay
  7. def run(self):
  8. print "%s delay for %s" %(self.name,self.delay)
  9. time.sleep(self.delay)
  10. c = 0
  11. while True:
  12. print "This is thread %s on line %s" %(self.name,c)
  13. c = c + 1
  14. if c == 3:
  15. print "End of thread %s" % self.name
  16. break
  17. t1 = test('Thread 1', 2)
  18. t2 = test('Thread 2', 2)
  19. t1.start()
  20. print "Wait t1 to end"
  21. t1.join()
  22. t2.start()
  23. print 'End of main'
  • thread模块不支持守护线程,thread模块中主线程退出的时候,所有的子线程不论是否还在工作,都会被强制结束,并且没有任何警告,也没有任何退出前的清理工作,比如:
  1. #coding=utf-8
  2. from thread import start_new_thread
  3. import time
  4. def myfunc(a,delay):
  5. print "I will calculate square of %s after delay for %s" %(a,delay)
  6. time.sleep(delay)
  7. print "calculate begins..."
  8. result = a*a
  9. print result
  10. return result
  11. start_new_thread(myfunc,(2,5))# 同时启动两个线程
  12. start_new_thread(myfunc,(6,8))
  13. time.sleep(1)

主线程没有考虑子线程就退出了,可以用threading解决,如下:

  1. import threading
  2. import time
  3. def myfunc(a,delay):
  4. print "I will calculate square of %s after delay for %s" %(a,delay)
  5. time.sleep(delay)
  6. print "calculate begins..."
  7. result = a*a
  8. print result
  9. return result
  10. t1=threading.Thread(target=myfunc,args=(2,5))
  11. t2=threading.Thread(target=myfunc,args=(6,8))
  12. print t1.isDaemon()
  13. print t2.isDaemon()
  14. t2.setDaemon(True)
  15. t1.start()
  16. t2.start()

建议四十七:使用Queue使多线程编程更安全

多线程从来就不是一个简单的问题,但是Queue却可以保障安全,而且不需要加锁,以生产者和消费者为例,看代码:

  1. #!usr/bin/python
  2. #coding=utf-8
  3. import Queue
  4. import threading
  5. import random
  6. writelock = threading.Lock() # 创建锁对象用于控制输出
  7. class Producer(threading.Thread):
  8. def __init__(self, q,name):
  9. super(Producer, self).__init__()
  10. self.q = q
  11. self.name = name
  12. print "Producer "+self.name+" Started"
  13. def run(self):
  14. while 1:
  15. if self.q.full(): # 队列满
  16. print 'Queue is full,producer wait!'
  17. else:
  18. value = random.randint(0,10)
  19. print self.name +" put value: " + str(value)+ "into queue"
  20. self.q.put((self.name+":"+str(value))) # 放入队列中
  21. class Consumer(threading.Thread): # 消费者
  22. def __init__(self, q,name):
  23. super(Consumer, self).__init__()
  24. self.q = q
  25. self.name = name
  26. print "Consumer "+self.name+" started\n "
  27. def run(self):
  28. while 1:
  29. if self.q.empty(): # 队列为空
  30. print 'queue is empty,consumer wait!'
  31. else:
  32. value = self.q.get() # 获取一个元素
  33. print self.name +"get value"+\
  34. value + " from queue"
  35. if __name__ == "__main__":
  36. q = Queue.Queue(10)
  37. p = Producer(q,"P1")
  38. p.start()
  39. p1 = Producer(q,"P2")
  40. p1.start()
  41. c1 = Consumer(q,"C1")
  42. c1.start()
  43. q.join()

python中的Queue模块提供了三种队列:

  • Queue.Queue():先进先出
  • Queue.LifoQueue():先进后出
  • Queue.PriorityQueue():优先级队列

参考:编写高质量代码--改善python程序的91个建议

编写高质量代码--改善python程序的建议(八)的更多相关文章

  1. 编写高质量代码--改善python程序的建议(六)

    原文发表在我的博客主页,转载请注明出处! 建议二十八:区别对待可变对象和不可变对象 python中一切皆对象,每一个对象都有一个唯一的标识符(id()).类型(type())以及值,对象根据其值能否修 ...

  2. 编写高质量代码--改善python程序的建议(七)

    原文发表在我的博客主页,转载请注明出处! 建议三十四:掌握字符串的基本用法 编程有两件事,一件是处理数值,另一件是处理字符串,在商业应用编程来说,处理字符串的代码超过八成,所以需要重点掌握. 首先有个 ...

  3. 编写高质量代码–改善python程序的建议(五)

    原文发表在我的博客主页,转载请注明出处! 建议二十三:遵循异常处理的几点基本原则 python中常用的异常处理语法是try.except.else.finally,它们可以有多种组合,语法形式如下: ...

  4. 编写高质量代码--改善python程序的建议(四)

    原文发表在我的博客主页,转载请注明出处! 建议十八:有节制的使用from...import语句 python提供了三种方式引入外部模块: import语句 from...import... __imp ...

  5. 编写高质量代码--改善python程序的建议(三)

    原文发表在我的博客主页,转载请注明出处! 建议十三:警惕eval()的安全漏洞 相信经常处理文本数据的同学对eval()一定是欲罢不能,他的使用非常简单: eval("1+1==2" ...

  6. 编写高质量代码–改善python程序的建议(二)

    原文发表在我的博客主页,转载请注明出处! 建议七:利用assert语句来发现问题断言(assert)在很多语言中都存在,它主要为调试程序服务,能够快速方便地检查程序的异常或者发现不恰当的输入等,可防止 ...

  7. 编写高质量代码--改善python程序的建议(一)

    原文发表在我的博客主页,转载请注明出处! 初衷 python是一个入门十分容易的编程语言,但是想要写好python却是一件不容易的事情,如果不是专业使用python的人,只是将python作为一个脚本 ...

  8. 编写高质量代码改善python程序91个建议学习01

    编写高质量代码改善python程序91个建议学习 第一章 建议1:理解pythonic的相关概念 狭隘的理解:它是高级动态的脚本编程语言,拥有很多强大的库,是解释从上往下执行的 特点: 美胜丑,显胜隐 ...

  9. 编写高质量代码 改善Python程序的91个建议 (读后 小记)

    此书是自己好久之前买的,当时总觉得Python语言中有各种trick, 总是要自己猝不及防的掉入到陷阱之中, 看了一些资料后发现了这本书,感觉很是不错,不过可惜自己平时总是杂事太多,总是找不到整块的时 ...

随机推荐

  1. 《SQL Server企业级平台管理实践》读书笔记——几个系统库的备份与恢复

    master数据库 master作为数据库的主要数据库,记录着SQL Server系统的所有系统级信息,例如登录用户.系统配置设置.端点和凭证以及访问其他数据服务器所需要的信息.master数据库还记 ...

  2. Linux Shell 06 数学运算

    1.let let "var+=1" let v2= echo $var,$v2 #2,4 a.只支持整数运算 b.基本支持所有运算符(包括++,——,**) c.表达式中使用变量 ...

  3. redis状态查看

      https://redis.readthedocs.org/en/latest/server/slowlog.html   https://redis.readthedocs.org/en/lat ...

  4. 分享一个linux环境下快速读取行数的命令

    最初是因为我需要计算一天的日志行数,如果用传统意义上的cat  a.log |wc -l的话因为是单线程,所以需要计算半小时的样子,后来同组的小伙伴教了我一个方法可以有效提高计算速度,将计算时间减半. ...

  5. Linux系统搭建LAMP平台

    知识背景(来自:百度百科): LAMP指的Linux(操作系统).Apache HTTP 服务器,MySQL(有时也指MariaDB,数据库软件) 和PHP(有时也是指Perl或Python) 的第一 ...

  6. 点击页面div弹窗以外隐藏的两种思路

    在本文为大家介绍两种思路实现点击页面其它地方隐藏该div,第一种是对document的click事件绑定事件处理程序.. 第一种思路分两步 第一步:对document的click事件绑定事件处理程序, ...

  7. hadoop debug script

    A Hadoop job may consist of many map tasks and reduce tasks. Therefore, debugging a Hadoop job is of ...

  8. [转]asp.net的ajax以及json

    本文转自:http://www.cnblogs.com/ensleep/p/3319756.html 来现在这家公司以前,从未接触过webform,以前在学校做的项目是php,java以及asp.ne ...

  9. selenium操作滚动条的几种方式

    1.操作滚动条到当前可见视图的元素位置 WebElement element = dr.findElement(By.id("4")); ((JavascriptExecutor) ...

  10. 【ASP.NET 进阶】无刷新上传图片之一:利用一般处理程序

    效果图: 源代码地址:https://github.com/YeXiaoChao/UploadThePic