Python实例(五)

如何使用多线程

场景:通过雅虎网站获取中国股市某支股票csv数据文件,现在要下载多支股票的csv数据,并将其转换为xml文件
如何使用线程来提高下载并处理的效率

解决方法:
使用标准库threading.Thread创建线程,在每一个线程中下载并转换一只股票数据

import csv
from xml.etree.ElementTree import Element, ElementTree
import requests
from StringIO import StringIO
from xml_pretty import pretty

def download(url): ##IO操作,多线程也提高不了多少效率
    response=requests.get(url,timeout=1)
    if response.ok:
        return StringIO(response.content) 
        ##StringIO是一种支持文件操作的内存对象

def csvToXml(scsv,fxml):  ##CPU密集型操作
    reader = csv.reader(scsv)
    headers = reader.next()
    headers= map(lambda h:h.replace(' ',''), headers)
    root = Element('Data')
    for row in reader:
        eRow=Element('Row')
        root.append(eRow)
        for tag,text in zip(headers,row):
            e=Element(tag)
            e.text=text
            eRow.append(e)
    pretty(root)
    et=ElementTree(root)
    et.write(fxml)

def handle(sid):
    print(Download...(%d)'% sid)
    url='http://table.finance.yahoo.com/table.csv?s=%s.sz'
    url %=str(sid).rjust(6,'0') ##字符串的构造
    rf=download(url)
    if rf is None:
        continue
    print('Convert to xml...(%d)'%sid)
    fname=str(sid).rjust(6,'0')+'.xml'
    with open (fname,'wb') as wf:
        csvToXml(rf,wf) 

from threading import Thread
##第一种进程创建方法
##t=Thread(target=handle,args=(1,))##直接创建Thread对象
##t.start()

##第二种方法
class MyThread(Thread):
    def __init__(self,sid):
        Thread.__init__(self)##调用父类的构造器
        self.sid=sid
    def run(self): ##新建进程程序的接口,相当于target
        handle(self.sid)

threads=[]
for i in range(1,11):
    t=MyThread(i)  ##得到一个函数对象
    threads.append(t)
    t.start()
for t in threads:
    t.join() ##让主线程等待每一个子进程运行完
##python中的进程只适合做IO型的操作,python中有GIL(全局解释器锁),
##导致python的只能一个进程被python解释器运行

如何实现线程间通信

场景:由于全局解释器锁的存在,多线程进行CPU密集型操作并不能提高执行效率,我们修改程序架构:
1. 使用多个DownloadThread线程进行下载
2. 使用一个ConvertThread线程进行转换(CPU密集型操作)
3. 下载线程把下载数据安全地传递给转换线程
以上一个案例继续

解决方法:
使用标准库中的Queue.Queue,它是一个线程安全的队列,Download线程把下载数据放入队列中,Convert线程从队列中提取数据

import csv
from xml.etree.ElementTree import Element, ElementTree
import requests
from StringIO import StringIO
from xml_pretty import pretty
from threading import Thread

##from collection import deque
##q=deque()  #全局变量,实现进程间的通信,但这不安全

from Queue import Queue ##线程安全的deque

class DownloadThread(Thread):
    def __init__(self,sid,queue):
        Thread.__init__(self)
        self.queue=queue
        self.sid=sid
        self.url='http://table.finance.yahoo.com/table.csv?s=%s.sz'
        self.url %=str(sid).rjust(6,'0')
    def download(self,url): 
        response=requests.get(url,timeout=1)
        if response.ok:
            return StringIO(response.content) 
    def run(self):
        data=self.download(self.url)
        self.queue.put((self.sid,data))

class ConvertThread(Thread):
    def __init__(self,queue):
        Thread.__init__(self)
        self.queue=queue
    def csvToXml(self,scsv,fxml):  
        reader = csv.reader(scsv)
        headers = reader.next()
        headers= map(lambda h:h.replace(' ',''), headers)
        root = Element('Data')
        for row in reader:
            eRow=Element('Row')
            root.append(eRow)
            for tag,text in zip(headers,row):
                e=Element(tag)
                e.text=text
                eRow.append(e)
        pretty(root)
        et=ElementTree(root)
        et.write(fxml)
    def run(self):
        while True:
            sid,data=self.queue.get()
            if sid==-1:
                break  ##进程结束标志
            if data:
                fname=str(sid).rjust(6,'0')+'.xml'
                with open (fname,'wb') as wf:
                    self.csvToXml(data,wf)  

q=Queue()
dThreads=[DownloadThread(i,q) for i in range(1,11)]
cThread=ConvertThread(q)
for t in dThreads:
    t.start()
    cThread.start()
for t in dThreads:
    t.join() ##等待下载进程全部执行完毕
q.put((-1,None)) ##设置结束标志通知转换进程结束

如何在线程间进行事件通知

场景:实现一个线程,将转换出的xml文件压缩打包,比如转换线程每生产100个xml文件就通知打包线程将它们打包成一个xxx.tgz文件,并删除xml文件。打包完成后,打包线程反过来通知转换线程,转换线程继续转换
继续上一个案例

解决方法:
线程间的事件通知,可以使用标准库中的Threading.Event:
1. 等待事件-端调用wait,等待事件
2. 通知事件-端调用set,通知事件

tar:tar是linux等下的打包工具,生成的包通常也用tar作为扩展名,其实tar只是负责打包,不一定有压缩,事实上可以压缩,也可以不压缩,通常你看到xxxx.tar.gz,就表示这个tar包是压缩的,并且使用的压缩算法是GNU ZIP,而xxxx.tar.bz2就表示这个包使用了bzip2算法进行压缩,当然这样的命名只是一种惯例,并非强制。简单地说,tar就仅是打包。

##接上一个案例,DownloadThread不变
import tarfile ##对文件进行压缩打包
import os
from threading import Event,Thread

class ConvertThread(Thread):
    def __init__(self,queue,cEvent,tEvent):
        Thread.__init__(self)
        self.queue=queue
        self.cEvent=cEvent ##转换完成事件
        self.tEvent=tEvent ##打包完成事件
    def csvToXml(self,scsv,fxml):  
        reader = csv.reader(scsv)
        headers = reader.next()
        headers= map(lambda h:h.replace(' ',''), headers)
        root = Element('Data')
        for row in reader:
            eRow=Element('Row')
            root.append(eRow)
            for tag,text in zip(headers,row):
                e=Element(tag)
                e.text=text
                eRow.append(e)
        pretty(root)
        et=ElementTree(root)
        et.write(fxml)
    def run(self):
        count = 0
        while True:
            sid,data=self.queue.get()
            if sid==-1:
                self.cEvent.set() ##当文件数量不足时也要打包
                self.tEvent.wait()
                break  ##进程结束标志
            if data:
                fname=str(sid).rjust(6,'0')+'.xml'
                with open (fname,'wb') as wf:
                    self.csvToXml(data,wf)  
                count+=1
                if count==5: ##凑齐5个xml文件时
                    self.cEvent.set() ##通知打包进程
                    self.tEvent.wait() ##等待打包进程的通知
                    self.tEvent.clear() 
                    count = 0

class TarThread(Thread):
    def __init__(self,cEvent,tEvent):
        Thread.__init__(self)
        self.count=0 ##用于tar包命名
        self.cEvent=cEvent ##转换完成事件
        self.tEvent=tEvent ##打包完成事件 
        self.setDaemon(True) ##设置守护进程
        ##其他线程退出之后,守护线程自动会退出(解决死循环) 
    def tarXML(self):
        self.count+=1
        tfname='%d.tgz'%self.count ##用数字来构造tar包名
        tf=tarfile.open(tfname,'w:gz') ##w写,gz表示压缩算法
        for fname in os.listdir('.'): ##遍历当前目录下所有文件
            if fname.endswith('.xml')
                tf.add(fname)
                os.remove(fname)
        tf.close()
        if not tf.members: ##如果当前目录下一个xml文件都没有
            os.remove(tfname) ##则删除空的tar包对象     
    def run(self):
        while True: ##死循环
            self.cEvent.wait() ##等待转换进程的通知,称阻塞
            self.tarXML() ##打包
            self.cEvent.clear() ##清空通知,清空阻塞
            self.tEvent.set() ##通知转换进程继续转换

if __name__='__main__':
    q=Queue()
    cEvent=Event() 
    tEvent=Event()
    cThread=ConvertThread(q,cEvent,tEvent)
    tThread=TarThread(cEvent,tEvent)
    tThread.start()
    for t in dThreads:
        t.start()
        cThread.start()
    for t in dThreads:
        t.join() ##等待下载进程全部执行完毕
    q.put((-1,None)) ##设置结束标志通知转换进程结束,(解决死循环)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python实例方法是定义在类中的方法,通过类的实例来调用。它们必须至少有一个参数self,用于表示实例本身。实例方法可以访问和修改实例的属性,并可以调用其他实例方法和静态方法。 例如,我们可以定义一个最简单的Python 3的类,其中包含一个实例方法method: ```python class MyClass: def method(self): print('我是实例方法', self) ``` 在这个例子中,method方法接受一个self参数,表示实例自身。我们可以通过创建MyClass的实例并调用method方法来使用实例方法。 ```python obj = MyClass() obj.method() ``` 输出将是:我是实例方法 \<__main__.MyClass object at 0x...> 需要注意的是,实例方法必须通过类的实例来调用,而不能直接通过类本身来调用。如果尝试直接通过类来调用实例方法,将会导致错误,因为Python无法给self参数传值。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [第7.14节 Python类中的实例方法详析](https://blog.csdn.net/LaoYuanPython/article/details/92420790)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [针对Python 实例方法、类方法和静态方法的详解](https://blog.csdn.net/qdPython/article/details/119353615)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值