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

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

python中解析XML文件最广为人知的两个模块是xml.dom.minidom和xml.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):表示把文件中的对象恢复为原来的对象
import cPickle as pickle
my_data = {"name" : "Python", "type" : "Language", "version" : "2.7.5"} fp = open("picklefile.dat","wb")
pickle.dump(my_data, fp)
fp.close() fp = open("picklefile.dat","rb")
out = pickle.load(fp)
fp.close()
print out
print type(out)

pickle拥有良好的特性:

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

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

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

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

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

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

gList = ['a','b','c','d','e','f','g']
def f():
gList[5]
return g()
def g():
return h()
def h():
del gList[2]
return i()
def i():
gList.append('i')
print gList[7]
if __name__ == '__main__':
try:
f()
except IndexError as ex:
print "Sorry,Exception occured,you accessed an element out of range"
print ex

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

    except IndexError as ex:
print "Sorry,Exception occured,you accessed an element out of range"
print ex
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。
import logging
logging.basicConfig(level = logging.DEBUG)
logger = logging.getLogger(__name__)
  • 为了方便地找出问题所在,logging的名字建议以模块或者class来命名
  • logging是线程安全的,不支持多进程写入同一个文件

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

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

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

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

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

import threading
import time
def myfunc(a,delay):
print "I will calculate square of %s after delay for %s" %(a,delay)
time.sleep(delay)
print "calculate begins..."
result = a*a
print result
return result
t1=threading.Thread(target=myfunc,args=(2,5))
t2=threading.Thread(target=myfunc,args=(6,8))
print t1.isDaemon()
print t2.isDaemon()
t2.setDaemon(True)
t1.start()
t2.start()

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

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

#!usr/bin/python
#coding=utf-8
import Queue
import threading
import random
writelock = threading.Lock() # 创建锁对象用于控制输出
class Producer(threading.Thread):
def __init__(self, q,name):
super(Producer, self).__init__()
self.q = q
self.name = name
print "Producer "+self.name+" Started"
def run(self):
while 1:
if self.q.full(): # 队列满
print 'Queue is full,producer wait!'
else:
value = random.randint(0,10)
print self.name +" put value: " + str(value)+ "into queue"
self.q.put((self.name+":"+str(value))) # 放入队列中 class Consumer(threading.Thread): # 消费者
def __init__(self, q,name):
super(Consumer, self).__init__()
self.q = q
self.name = name
print "Consumer "+self.name+" started\n "
def run(self):
while 1:
if self.q.empty(): # 队列为空
print 'queue is empty,consumer wait!'
else:
value = self.q.get() # 获取一个元素
print self.name +"get value"+\
value + " from queue" if __name__ == "__main__":
q = Queue.Queue(10)
p = Producer(q,"P1")
p.start()
p1 = Producer(q,"P2")
p1.start()
c1 = Consumer(q,"C1")
c1.start()
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. 【mysql】关于临时表

    mysql官方的介绍 In some cases, the server creates internal temporary tables while processing queries. Suc ...

  2. 在VS项目中通过GIT生成版本号作为编译版本号

    上一篇博客写了如何在 .Net 项目使用 SVN 作为版本控制工具时生成与代码对应的组件版本号.虽然在公司一直使用 SVN ,但我却对 GIT 情有独钟(可能要归功于那段捣鼓 ROM 的时光),但少有 ...

  3. DNS报文格式

    原文链接地址:http://blog.chinaunix.net/uid-24875436-id-3088461.html DNS报文格式(借个图贴过来):     说明一下:并不是所有DNS报文都有 ...

  4. python代码学习day03-序列化学习pickle及json

    #!/usr/bin/env python #coding:utf8 import pickle,json import datetime dic1 = {'name':'alex', 'age':4 ...

  5. iptables 详解

    一:前言   防火墙,其实说白了讲,就是用于实现Linux下访问控制的功能的,它分为硬件的或者软件的防火墙两种.无论是在哪个网络中,防火墙工作的地方一定是在网络的边缘.而我们的任务就是需要去定义到底防 ...

  6. apache加载php配置

    #载入php模块和ini路径,以及凡是.php开头的以它来处理 LoadModule php5_module E:/server/php/php5apache2_2.dll PHPIniDir &qu ...

  7. Excel scientific notation issue

        This is a known issue, you can find more in internet. Excel will treat text(can display with num ...

  8. 动手学习TCP:4种定时器

    上一篇中介绍了TCP数据传输中涉及的一些基本知识点.本文让我们看看TCP中的4种定时器. TCP定时器 对于每个TCP连接,TCP管理4个不同的定时器,下面看看对4种定时器的简单介绍. 重传定时器使用 ...

  9. js立即执行函数: (function ( ){...})( ) 与 (function ( ){...}( )) 有区别?

    没有区别. 你需要明白 IIFE 的原理,我简单说一下: function foo() {...} // 这是定义,Declaration:定义只是让解释器知道其存在,但是不会运行. foo(); / ...

  10. Android系列之网络(一)----使用HttpClient发送HTTP请求(通过get方法获取数据)

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...