python 多线程
多线程类似于同时执行多个不同程序,多线程运行有如下优点:
使用线程可以把占据长时间的程序中的任务放到后台去处理。
用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
程序的运行速度可能加快
在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。
指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。
线程可以被抢占(中断)。
在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) — 这就是线程的退让。
开始学习Python线程
Python中使用线程有两种方式:函数或者用类来包装线程对象。
函数式:调用 thread 模块中的start_new_thread()函数来产生新线程。语法如下:
thread.start_new_thread ( function, args[, kwargs] )
参数说明:
function – 线程函数。
args – 传递给线程函数的参数,他必须是个tuple类型。
kwargs – 可选参数。
实例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-importthreadimporttime# 为线程定义一个函数defprint_time(threadName, delay): count = 0whilecount < 5: time.sleep(delay)count += 1print”%s: %s” % (threadName, time.ctime(time.time()))# 创建两个线程try: thread.start_new_thread(print_time, (“Thread-1″, 2, ))thread.start_new_thread(print_time, (” Thread -2″, 4, ))except: print”Error: unable to start thread”while1: pass
执行以上程序输出结果如下:
Thread-1: Thu Jan 22 15:42:17 2009 Thread-1: Thu Jan 22 15:42:19 2009 Thread-2: Thu Jan 22 15:42:19 2009 Thread-1: Thu Jan 22 15:42:21 2009 Thread-2: Thu Jan 22 15:42:23 2009 Thread-1: Thu Jan 22 15:42:23 2009 Thread-1: Thu Jan 22 15:42:25 2009 Thread-2: Thu Jan 22 15:42:27 2009 Thread-2: Thu Jan 22 15:42:31 2009 Thread-2: Thu Jan 22 15:42:35 2009
线程的结束一般依靠线程函数的自然结束;也可以在线程函数中调用thread.exit(),他抛出SystemExit exception,达到退出线程的目的。
线程模块
Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。
thread 模块提供的其他方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
run(): 用以表示线程活动的方法。
start(): 启动线程活动。
join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。
使用Threading模块创建线程
使用Threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法:
实例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-importthreadingimporttimeexitFlag = 0classmyThread(threading.Thread): #继承父类threading.Threaddef__init__(self, threadID, name, counter): threading.Thread.__init__(self)self.threadID = threadIDself.name = nameself.counter = counterdefrun(self): #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数 print”Starting ” + self.nameprint_time(self.name, self.counter, 5)print”Exiting ” + self.namedefprint_time(threadName, delay, counter): whilecounter: ifexitFlag: threading.Thread.exit()time.sleep(delay)print”%s: %s” % (threadName, time.ctime(time.time()))counter -= 1# 创建新线程thread1 = myThread(1, “Thread-1”, 1)thread2 = myThread(2, “Thread-2″, 2)# 开启线程thread1.start()thread2.start()print”Exiting Main Thread”
以上程序执行结果如下;
Starting Thread-1 Starting Thread-2 Exiting Main Thread Thread-1: Thu Mar 21 09:10:03 2013 Thread-1: Thu Mar 21 09:10:04 2013 Thread-2: Thu Mar 21 09:10:04 2013 Thread-1: Thu Mar 21 09:10:05 2013 Thread-1: Thu Mar 21 09:10:06 2013 Thread-2: Thu Mar 21 09:10:06 2013 Thread-1: Thu Mar 21 09:10:07 2013 Exiting Thread-1 Thread-2: Thu Mar 21 09:10:08 2013 Thread-2: Thu Mar 21 09:10:10 2013 Thread-2: Thu Mar 21 09:10:12 2013 Exiting Thread-2
线程同步
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。如下:
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。
考虑这样一种情况:一个列表里所有元素都是0,线程”set”从后向前把所有元素改成1,而线程”print”负责从前往后读取列表并打印。
那么,可能线程”set”开始改的时候,线程”print”便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。
锁有两种状态——锁定和未锁定。每当一个线程比如”set”要访问共享数据时,必须先获得锁定;如果已经有别的线程比如”print”获得锁定了,那么就让线程”set”暂停,也就是同步阻塞;等到线程”print”访问完毕,释放锁以后,再让线程”set”继续。
经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。
实例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-importthreadingimporttimeclassmyThread(threading.Thread): def__init__(self, threadID, name, counter): threading.Thread.__init__(self)self.threadID = threadIDself.name = nameself.counter = counterdefrun(self): print”Starting ” + self.name# 获得锁,成功获得锁定后返回True# 可选的timeout参数不填时将一直阻塞直到获得锁定# 否则超时后将返回FalsethreadLock.acquire()print_time(self.name, self.counter, 3)# 释放锁threadLock.release()defprint_time(threadName, delay, counter): whilecounter: time.sleep(delay)print”%s: %s” % (threadName, time.ctime(time.time()))counter -= 1threadLock = threading.Lock()threads = []# 创建新线程thread1 = myThread(1, “Thread-1”, 1)thread2 = myThread(2, “Thread-2″, 2)# 开启新线程thread1.start()thread2.start()# 添加线程到线程列表threads.append(thread1)threads.append(thread2)# 等待所有线程完成fortinthreads: t.join()print”Exiting Main Thread”
线程优先级队列( Queue)
Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。
Queue模块中的常用方法:
Queue.qsize() 返回队列的大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.full 与 maxsize 大小对应
Queue.get([block[, timeout]])获取队列,timeout等待时间
Queue.get_nowait() 相当Queue.get(False)
Queue.put(item) 写入队列,timeout等待时间
Queue.put_nowait(item) 相当Queue.put(item, False)
Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
Queue.join() 实际上意味着等到队列为空,再执行别的操作
实例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-importQueueimportthreadingimporttimeexitFlag = 0classmyThread(threading.Thread): def__init__(self, threadID, name, q): threading.Thread.__init__(self)self.threadID = threadIDself.name = nameself.q = qdefrun(self): print”Starting ” + self.nameprocess_data(self.name, self.q)print”Exiting ” + self.namedefprocess_data(threadName, q): whilenotexitFlag: queueLock.acquire()ifnotworkQueue.empty(): data = q.get()queueLock.release()print”%s processing %s” % (threadName, data)else: queueLock.release()time.sleep(1)threadList = [“Thread-1”, “Thread-2”, “Thread-3”]nameList = [“One”, “Two”, “Three”, “Four”, “Five”]queueLock = threading.Lock()workQueue = Queue.Queue(10)threads = []threadID = 1# 创建新线程fortNameinthreadList: thread = myThread(threadID, tName, workQueue)thread.start()threads.append(thread)threadID += 1# 填充队列queueLock.acquire()forwordinnameList: workQueue.put(word)queueLock.release()# 等待队列清空whilenotworkQueue.empty(): pass# 通知线程是时候退出exitFlag = 1# 等待所有线程完成fortinthreads: t.join()print”Exiting Main Thread”
以上程序执行结果:
Starting Thread-1 Starting Thread-2 Starting Thread-3 Thread-1 processing One Thread-2 processing Two Thread-3 processing Three Thread-1 processing Four Thread-2 processing Five Exiting Thread-3 Exiting Thread-1 Exiting Thread-2 Exiting Main Thread
Python XML解析
什么是XML?
XML 指可扩展标记语言(e X tensible M arkup L anguage)。 你可以通过本站学习XML教程
XML 被设计用来传输和存储数据。
XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这些部件加以标识。
它也是元标记语言,即定义了用于定义其他与特定领域有关的、语义的、结构化的标记语言的句法语言。
python对XML的解析
常见的XML编程接口有DOM和 SAX ,这两种接口处理XML文件的方式不同,当然使用场合也不同。
python有三种方法解析XML,SAX,DOM,以及ElementTree:
1.SAX (simple API for XML )
python 标准库包含SAX解析器,SAX用事件驱动模型,通过在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。
2.DOM(Document Object Model)
将XML数据在内存中解析成一个树,通过对树的操作来操作XML。
3.ElementTree(元素树)
ElementTree就像一个轻量级的DOM,具有方便友好的API。代码可用性好,速度快,消耗内存少。
注: 因DOM需要将XML数据映射到内存中的树,一是比较慢,二是比较耗内存,而SAX流式读取XML文件,比较快,占用内存少,但需要用户实现回调函数(handler)。
本章节使用到的XML实例文件movies.xml内容如下:
<collection shelf="New Arrivals"><movie title="Enemy Behind">
< type >War, Thriller</type>
<format>DVD</format>
<year>2003</year>
<rating>PG</rating>
<stars>10</stars>
<description>Talk about a US-Japan war</description></movie><movie title="Transformers">
<type>Anime, Science Fiction</type>
<format>DVD</format>
<year>1989</year>
<rating>R</rating>
<stars>8</stars>
<description>A schientific fiction</description></movie>
<movie title="Trigun">
<type>Anime, Action</type>
<format>DVD</format>
<episodes>4</episodes>
<rating>PG</rating>
<stars>10</stars>
<description>Vash the Stampede!</description></movie><movie title="Ishtar">
<type>Comedy</type>
<format>VHS</format>
<rating>PG</rating>
<stars>2</stars>
<description>Viewable boredom</description></movie></collection>
python使用SAX解析xml
SAX是一种基于事件驱动的API。
利用SAX解析XML文档牵涉到两个部分:解析器和事件处理器。
解析器负责读取XML文档,并向事件处理器发送事件,如元素开始跟元素结束事件;
而事件处理器则负责对事件作出相应,对传递的XML数据进行处理。
<psax适于处理下面的问题:< p=”” style=”color: rgb(51, 51, 51); font-family: “Helvetica Neue”, Helvetica, “PingFang SC”, “Hiragino Sans GB”, “Microsoft YaHei”, “Noto Sans CJK SC”, “WenQuanYi Micro Hei”, Arial, sans-serif; font-size: 12px; white-space: normal;”>
1、对大型文件进行处理;
2、只需要文件的部分内容,或者只需从文件中得到特定信息。
3、想建立自己的对象模型的时候。
在python中使用sax方式处理xml要先引入xml.sax中的parse函数,还有xml.sax.handler中的ContentHandler。
ContentHandler类方法介绍
characters(content)方法
调用时机:
从行开始,遇到标签之前,存在字符,content的值为这些字符串。
从一个标签,遇到下一个标签之前, 存在字符,content的值为这些字符串。
从一个标签,遇到行结束符之前,存在字符,content的值为这些字符串。
标签可以是开始标签,也可以是结束标签。
startDocument()方法
文档启动的时候调用。
endDocument()方法
解析器到达文档结尾时调用。
startElement(name, attrs)方法
遇到XML开始标签时调用,name是标签的名字,attrs是标签的属性值字典。
endElement(name)方法
遇到XML结束标签时调用。
make_parser方法
以下方法创建一个新的解析器对象并返回。
xml.sax.make_parser( [parser_list] )
参数说明:
parser_list – 可选参数,解析器列表
parser方法
以下方法创建一个 SAX 解析器并解析xml文档:
xml.sax.parse( xmlfile, contenthandler[, errorhandler])
参数说明:
xmlfile – xml文件名
content Handler – 必须是一个ContentHandler的对象
errorhandler – 如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象
parseString方法
parseString方法创建一个XML解析器并解析xml字符串:
xml.sax.parseString(xmlstring, contenthandler[, errorhandler])
参数说明:
xmlstring – xml字符串
contenthandler – 必须是一个ContentHandler的对象
errorhandler – 如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象
Python 解析XML实例
#!/usr/bin/python# -*- coding: UTF-8 -*-import xml.saxclass MovieHandler( xml.sax.ContentHandler ): def __init__(self): self.CurrentData = "" self.type = "" self.format = "" self.year = "" self.rating = "" self.stars = "" self.description = "" # 元素开始事件处理 def startElement(self, tag, attributes): self.CurrentData = tag if tag == "movie": print "*****Movie*****" title = attributes["title"] print "Title:", title # 元素结束事件处理 def endElement(self, tag): if self.CurrentData == "type": print "Type:", self.type elif self.CurrentData == "format": print "Format:", self.format elif self.CurrentData == "year": print "Year:", self.year elif self.CurrentData == "rating": print "Rating:", self.rating elif self.CurrentData == "stars": print "Stars:", self.stars elif self.CurrentData == "description": print "Description:", self.description self.CurrentData = "" # 内容事件处理 def characters(self, content): if self.CurrentData == "type": self.type = content elif self.CurrentData == "format": self.format = content elif self.CurrentData == "year": self.year = content elif self.CurrentData == "rating": self.rating = content elif self.CurrentData == "stars": self.stars = content elif self.CurrentData == "description": self.description = content if ( __name__ == "__main__"): # 创建一个 XMLReader parser = xml.sax.make_parser() # turn off namepsaces parser.setFeature(xml.sax.handler.feature_namespaces, 0) # 重写 ContextHandler Handler = MovieHandler() parser.setContentHandler( Handler ) parser.parse("movies.xml")
以上代码执行结果如下:
*****Movie*****Title: Enemy BehindType: War, ThrillerFormat: DVDYear: 2003Rating: PGStars: 10Description: Talk about a US-Japan war*****Movie*****Title: TransformersType: Anime, Science FictionFormat: DVDYear: 1989Rating: RStars: 8Description: A schientific fiction*****Movie*****Title: TrigunType: Anime, ActionFormat: DVDRating: PGStars: 10Description: Vash the Stampede!*****Movie*****Title: IshtarType: ComedyFormat: VHSRating: PGStars: 2Description: Viewable boredom
完整的 SAX API 文档请查阅Python SAX APIs
使用xml.dom解析xml
文件对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展置标语言的标准编程接口。
一个 DOM 的解析器在解析一个 XML 文档时,一次性读取整个文档,把文档中所有元素保存在内存中的一个树结构里,之后你可以利用DOM 提供的不同的函数来读取或修改文档的内容和结构,也可以把修改过的内容写入xml文件。
python中用xml.dom.minidom来解析xml文件,实例如下:
#!/usr/bin/python# -*- coding: UTF-8 -*-from xml.dom.minidom import parseimport xml.dom.minidom# 使用minidom解析器打开 XML 文档DOMTree = xml.dom.minidom.parse("movies.xml")collection = DOMTree.documentElementif collection.hasAttribute("shelf"):
print "Root element : %s" % collection.getAttribute("shelf")# 在集合中获取所有电影movies = collection. getElementsByTagName ("movie")# 打印每部电影的详细信息for movie in movies:
print "*****Movie*****"
if movie.hasAttribute("title"):
print "Title: %s" % movie.getAttribute("title")
type = movie.getElementsByTagName('type')[0]
print "Type: %s" % type.childNodes[0].data
format = movie.getElementsByTagName('format')[0]
print "Format: %s" % format.childNodes[0].data
rating = movie.getElementsByTagName('rating')[0]
print "Rating: %s" % rating.childNodes[0].data
description = movie.getElementsByTagName('description')[0]
print "Description: %s" % description.childNodes[0].data
以上程序执行结果如下:
Root element : New Arrivals*****Movie*****Title: Enemy BehindType: War, ThrillerFormat: DVDRating: PGDescription: Talk about a US-Japan war*****Movie*****Title: TransformersType: Anime, Science FictionFormat: DVDRating: RDescription: A schientific fiction*****Movie*****Title: TrigunType: Anime, ActionFormat: DVDRating: PGDescription: Vash the Stampede!*****Movie*****Title: IshtarType: ComedyFormat: VHSRating: PG
Description: Viewable boredom
Python 提供了多个图形开发界面的库,几个常用 Python GUI 库如下:
Tkinter: Tkinter 模块(Tk 接口)是 Python 的标准 Tk GUI 工具包的接口 .Tk 和 Tkinter 可以在大多数的 Unix 平台下使用,同样可以应用在 Windows 和 Macintosh 系统里。Tk8.0 的后续版本可以实现本地窗口风格,并良好地运行在绝大多数平台中。
wxPython: wxPython 是一款开源软件,是 Python 语言的一套优秀的 GUI 图形库,允许 Python 程序员很方便的创建完整的、功能键全的 GUI 用户界面。
Jython: Jython 程序可以和 Java 无缝集成。除了一些标准模块,Jython 使用 Java 的模块。Jython 几乎拥有标准的Python 中不依赖于 C 语言的全部模块。比如,Jython 的用户界面将使用 Swing,AWT或者 SWT。Jython 可以被动态或静态地编译成 Java 字节码。
Tkinter 编程
Tkinter 是 Python 的标准 GUI 库。Python 使用 Tkinter 可以快速的创建 GUI 应用程序。
由于 Tkinter 是内置到 python 的安装包中、只要安装好 Python 之后就能 import Tkinter 库、而且 IDLE 也是用 Tkinter 编写而成、对于简单的图形界面 Tkinter 还是能应付自如。
创建一个GUI程序
1、导入 Tkinter 模块
2、创建控件
3、指定这个控件的 master, 即这个控件属于哪一个
4、告诉 GM(geometry manager) 有一个控件产生了。
实例:
#!/usr/bin/python# -*- coding: UTF-8 -*-import Tkintertop = Tkinter.Tk()# 进入消息循环top.mainloop()
以上代码执行结果如下图:
实例2:
#!/usr/bin/python# -*- coding: UTF-8 -*-from Tkinter import * # 导入 Tkinter 库root = Tk() # 创建窗口对象的背景色 # 创建两个列表li = ['C','python','php','html','SQL','java']movie = ['CSS','jQuery','Bootstrap']listb = Listbox(root) # 创建两个列表组件listb2 = Listbox(root)for item in li: # 第一个小部件插入数据 listb.insert(0,item)for item in movie: # 第二个小部件插入数据 listb2.insert(0,item)listb.pack() # 将小部件放置到主窗口中listb2.pack()root.mainloop() # 进入消息循环
以上代码执行结果如下图:
Tkinter 组件
Tkinter的提供各种控件,如按钮,标签和文本框,一个GUI应用程序中使用。这些控件通常被称为控件或者部件。
目前有15种Tkinter的部件。我们提出这些部件以及一个简短的介绍,在下面的表:
控件 | 描述 |
---|---|
Button | 按钮控件;在程序中显示按钮。 |
Canvas | 画布控件;显示图形元素如线条或文本 |
Checkbutton | 多选框控件;用于在程序中提供多项选择框 |
Entry | 输入控件;用于显示简单的文本内容 |
Frame | 框架控件;在屏幕上显示一个矩形区域,多用来作为容器 |
Label | 标签控件;可以显示文本和位图 |
Listbox | 列表框控件;在Listbox窗口小部件是用来显示一个字符串列表给用户 |
Menubutton | 菜单按钮控件,由于显示菜单项。 |
Menu | 菜单控件;显示菜单栏,下拉菜单和弹出菜单 |
Message | 消息控件;用来显示多行文本,与label比较类似 |
Radiobutton | 单选按钮控件;显示一个单选的按钮状态 |
Scale | 范围控件;显示一个数值刻度,为输出限定范围的数字区间 |
Scrollbar | 滚动条控件,当内容超过可视化区域时使用,如列表框。. |
Text | 文本控件;用于显示多行文本 |
Toplevel | 容器控件;用来提供一个单独的对话框,和Frame比较类似 |
Spinbox | 输入控件;与Entry类似,但是可以指定输入范围值 |
PanedWindow | PanedWindow是一个窗口布局管理的插件,可以包含一个或者多个子控件。 |
LabelFrame | labelframe 是一个简单的容器控件。常用与复杂的窗口布局。 |
tkMessageBox | 用于显示你应用程序的消息框。 |
标准属性
标准属性也就是所有控件的共同属性,如大小,字体和颜色等等。
属性 | 描述 |
Dimension | 控件大小; |
Color | 控件颜色; |
Font | 控件字体; |
Anchor | 锚点; |
Relief | 控件样式; |
Bitmap | 位图; |
Cursor | 光标; |
几何管理
Tkinter控件有特定的几何状态管理方法,管理整个控件区域组织,一下是Tkinter公开的几何管理类:包、网格、位置
几何方法 | 描述 |
pack() | 包装; |
grid() | 网格; |
place() | 位置; |