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() | 位置; |