专科生也能用!最受欢迎的一键生成论文工具 —— 千笔·专业论文写作工具

核心内容摘要

颠覆式网盘加速方案:Online-disk-direct-link-download-assistant全攻略
MedGemma 1.5快速上手:首次使用必知的5个CoT观察技巧与避坑指南

NEURAL MASK Java后端集成指南:SpringBoot微服务调用视觉重构API

ä½ å¥½æˆ‘æ˜¯å®‰ç„¶æ— è™�。和我一起为高质é‡�人生而ä¸�æ‡ˆå¥‹æ–—ã€‚æ–‡ç« ç›®å½•å¤šçº¿ç¨‹å¤šçº¿ç¨‹å¼•å…¥2ç§�æ–¹å¼�创建线程线程间的通信并å�‘æ�§åˆ¶-Semaphoreçº¿ç¨‹æ± done()result()cancel()as_completed()map()wait()with语å�¥å¤šçº¿ç¨‹å¤šçº¿ç¨‹å¼•入之å‰�我有写过一篇关äº� å¤šçº¿ç¨‹å’Œå¤šè¿›ç¨‹çš„æ–‡ç« :31天Python入门——第24天:挑战一å�£æ°”把多线程和多进程讲æ˜�白没看过的è€�é“�, å�¯ä»¥å…ˆé˜…读上é�¢è¿™ç¯‡æ–‡ç« , è¿™æ ·çš„è¯�就有了基础, 方便下é�¢å†…å®¹çš„å­¦ä¹ .多线程: 适用äº� IO 密集å�‹ä»£ç �多进程: 适用äº� CPU 密集å�‹ä»£ç �爬虫, 主è¦�是 网络IO.GIL é�‡åˆ° IOæ“�作的时候, 会释放.ok, 有了å‰�é�¢çš„基础, 下é�¢æˆ‘们看这段 多线程代ç �:print(主线程执行代ç �)# ä»� threading 库中导入Threadç±»fromthreadingimportThreadfromtimeimportsleep# 定义一个函数作为新线程执行的入å�£å‡½æ•°defthreadFunc(arg1,arg

:print(�线程 开始)print(f线程函数�数是{arg1},{arg2})sleep(

print(å­�线程 结æ�Ÿ)# 创建 Thread 类的å®�例对象threadThread(# target å�‚æ•° 指定 新线程è¦�执行的函数# 注æ„�这里指定的函数对象å�ªèƒ½å†™ä¸€ä¸ªå��å­—ä¸�能å��é�¢åŠ æ‹¬å�·# 如æ�œåŠ æ‹¬å�·å°±æ˜¯ç›´æ�¥åœ¨å½“å‰�线程调用执行而ä¸�是在新线程中执行了targetthreadFunc,# 如æ�œ 新线程函数需è¦�å�‚数在 args里é�¢å¡«å…¥å�‚æ•°# 注æ„�å�‚数是元组 如æ�œå�ªæœ‰ä¸€ä¸ªå�‚æ•°å��é�¢è¦�有逗å�·åƒ�è¿™æ · args(å�‚æ•°1,)args(å�‚æ•°1,å�‚æ•°

)# 执行start 方法就会创建新线程# 并且新线程会å�»æ‰§è¡Œå…¥å�£å‡½æ•°é‡Œé�¢çš„代ç �。# 这时候 这个进程 有两个线程了。thread.start()# 主线程的代ç �执行 å­�线程对象的join方法# 就会等待å­�线程结æ�Ÿæ‰�继续执行下é�¢çš„代ç �thread.join()print(主线程结æ�Ÿ)这里有两点需è¦�强调说æ˜�:创建了 Thread å®�例对象之å��, 这时新的线程还没有创建, 需è¦�调用 Thread 对象的 start()方法, è¿™æ ·æ–°çš„çº¿ç¨‹æ‰�创建æˆ�功, 并开始执行 threadFunc å…¥å�£å‡½æ•°é‡Œé�¢çš„代ç �.有的时候, 一个线程需è¦�等待其他线程结æ�Ÿ, 比如需è¦�æ ¹æ�®å…¶ä»–线程è¿�行结æ�Ÿå��的结æ�œè¿›è¡Œå¤„ç�†, 这时å�¯ä»¥ä½¿ç”¨ Thread 对象的 join() 方法, 等待对应的线程完æˆ�, æ‰�执行å��续代ç �.好的, ç�†è§£äº†ä¸Šé�¢çš„代ç �之å��, å°±å�¯ä»¥ç»§ç»­å��é�¢çš„å­¦ä¹ äº†.2ç§�æ–¹å¼�创建线程写爬虫的时候, 有一个简å�•网站å�—里é�¢æœ‰ 列表页和详情页.importthreadingdefget_list_html(url):print(开始è�·å�–列表页)time.sleep(

# 模拟网络请求.print(已���到��列表页)defget_detail_html(url):print(开始��详情页)time.sleep(

# 模拟网络请求.print(已���到��详情页)urlwww.baidu.comt1threading.Thread(targetget_list_html,args(url,))t2threading.Thread(targetget_detail_html,args(url,))t

start()t

start()t

join()t

join()è¿�行结æ�œæ˜¯:开始è�·å�–列表页 开始è�·å�–详情页 å·²ç»�è�·å�–到列表页 å·²ç»�è�·å�–到详情页这其å®�是 函数å¼�创建线程的方å¼�, 还有一ç§� é�¢å�‘对象å¼� 通过继承 threading.Thread 类并é‡�写 run()方法的方å¼�.ok, 下é�¢æˆ‘们用é�¢å�‘对象å¼�的方å¼�对上é�¢çš„代ç �进行改写:importthreadingimporttimeclassListHtmlThread(threading.Thread):def__init__(self,url):super().__init__()self.urlurl# ä¿�å­˜å�‚数到å®�例å±�性defrun(self):print(开始è�·å�–列表页)time.sleep(

# 模拟网络请求print(已���到列表页)classDetailHtmlThread(threading.Thread):def__init__(self,url):super().__init__()self.urlurldefrun(self):print(开始��详情页)time.sleep(

# 模拟网络请求print(已���到详情页)# 使用urlwww.baidu.comt1ListHtmlThread(url)t2DetailHtmlThread(url)t

start()t

start()t

join()t

join()è�ªæ˜�çš„è€�é“�å�¯èƒ½å·²ç»�å�‘ç�°äº†, å…¶å®� run()方法 就是在线程中è¿�行的代ç �体, 也就相当äº� 函数å¼�创建线程时的 ä¼ ç»™target的那个函数.之å‰�说过 daemon线程 的概念(也å�«å®ˆæŠ¤çº¿ç¨‹), å‰�é�¢å‡½æ•°å¼�代ç �è¿�è¡Œä¹Ÿæ˜¯ä¸€æ ·, 主线程得等2ç§’å��å­�线程结æ�Ÿ, 主线程æ‰�能结æ�Ÿ.è¿™æ˜¯å› ä¸º:Python 程åº�中当所有的 é��daemon线程 都结æ�Ÿäº†, 整个程åº�æ‰�会结æ�Ÿ.主线程是é��daemon线程, å�¯åŠ¨çš„å­�线程缺çœ�也是 é��daemon线程.所以, è¦�等到主线程和å­�线程都结æ�Ÿ, 程åº�æ‰�会结æ�Ÿ.我们å�¯ä»¥åœ¨åˆ›å»ºå­�线程的时候, 设置daemonå�‚数值为 True, 比如之å‰�的代ç �改写:# æ–¹å¼�一: 在定义å®�例对象的时候设置 daemonTruethreadThread(targetthreadFunc,daemonTrue# 设置新线程为daemon线程)# æ–¹å¼�二: 在线程å�¯åЍå‰�调用 .setDeamon(True)方法threadThread(targetthreadFunc)thread.setDaemon(True)# 需è¦�注æ„�的点: 守护线程è¦�在线程å�¯åЍ之å‰�设置thread.start()好的, ç�†è§£äº†ä¸Šé�¢å‡½æ•°å¼�çš„, 那么é�¢å�‘对象å¼�的就很简å�•了, å�ªè¦�è¿™æ ·:...t1ListHtmlThread(url)t2DetailHtmlThread(url)t

setDaemon(True)t

setDaemon(True)t

start()t

start()线程间的通信在之å‰�çš„æ–‡ç« ä¸­æˆ‘ä»¬æœ‰è¯´è¿‡ 通过é”�机制æ�¥å¯¹å…±äº«æ•°æ�®çš„访问æ�§åˆ¶, 忘记的è€�é“�å�¯ä»¥å†�å›�顾一下:31天Python入门——第24天:挑战一å�£æ°”把多线程和多进程讲æ˜�白好的, ç�°åœ¨æˆ‘们知é�“:在多线程ç�¯å¢ƒä¸­, 所有线程共享å�Œä¸€ä¸ªè¿›ç¨‹çš„内存空间. 但是由äº�线程切æ�¢å¾—é��常快, å› æ­¤å¤šä¸ªçº¿ç¨‹å�Œæ—¶è®¿é—®å’Œä¿®æ”¹å…±äº«èµ„æº�æ—¶, 很容易出ç�°æ•°æ�®ç«�争问题, 这就å�¯èƒ½å�‘生数æ�®ä¸�一致的错误.为了é�¿å…�è¿™ç§�问题, 需è¦�使用é”�或其他的å�Œæ­¥æœºåˆ¶ æ�¥ç¡®ä¿�å�Œä¸€æ—¶é—´å�ªæœ‰ä¸€ä¸ªçº¿ç¨‹å�¯ä»¥è®¿é—®å…±äº«èµ„æº�.æ�¥ä¸‹æ�¥, 我们使用本身就是线程安全的数æ�®ç»“æ�„ Queue, 通过它æ�¥ä»£æ›¿å…±äº«æ•°æ�®çš„访问.ok, 下é�¢æœ‰ä¸€ä¸ªç”Ÿäº§è€…消费者模å�‹ (生产者线程在生产, 消费者线程在消费) :importthreadingimporttimefromqueueimportQueue,Empty# 通过安全的队列æ�¥é€šä¿¡.list_statusFalse# 生产者classGetListHtml(threading.Thread):def__init__(self,url_queue:Queue):super(GetListHtml,self).__init__()self.url_queueurl_queuedefrun(self):print(开始è�·å�–列表页)try:foriinrange(

:time.sleep(

0.

# 模拟网络请求.urlfwww.yrx.com/{i}# Queue.join()里有一个计数器, 这一个计数器会在put的时候åŠ

在�一次task_done的时候�1self.url_queue.put(url)print(已���到��列表页)finally:globallist_status list_statusTrue# 消费者classGetDetailHtml(threading.Thread):def__init__(self,url_queue:Queue):super(GetDetailHtml,self).__init__()self.url_queueurl_queuedefrun(self):print(开始��详情页)whileTrue:try:urlself.url_queue.get(timeout

# Queue.join()里有一个计数器, 这一个计数器会在put的时候åŠ

在�一次task_done的时候�1self.url_queue.task_done()print(f已���到��详情页-{url})time.sleep(

# 模拟网络请求.# 当消费者消费比生产者快的时候,Queue里�的一定会出�为空的情况, 这时候消费者��消费会出�出�Empty异常exceptEmpty:# 想一个�法, 让程�知�生产者已�结�了.globallist_statusprint(list_status)iflist_status:print(详情页已抓完)breakif__name____main__:start_timetime.time()url_queueQueue(

# 一个生产者t1GetListHtml(url_queue)# 两个消费者t2GetDetailHtml(url_queue)t3GetDetailHtml(url_queue)# 创建线程并�动t

start()t

start()t

start()# 主线程阻�等待�线程执行完毕t

join()t

join()t

join()# join: 阻å¡�队列. 在主线程阻å¡�.# join里有一个计数器, 这一个计数器会在put的时候åŠ

在�一次task_done的时候�

# 当这个计数器是0的时候. 它会释放这个阻�.url_queue.join()print(time.time()-start_time)�行结�是:开始��列表页 开始��详情页 开始��详情页 已���到��列表页 已���到��详情页-www.yrx.com/0 False 已���到��列表页 已���到��详情页-www.yrx.com/1 已���到��列表页 已���到��详情页-www.yrx.com/2 已���到��列表页已���到��详情页-www.yrx.com/3 已���到��列表页已���到��详情页-www.yrx.com/4 已���到��列表页 已���到��详情页-www.yrx.com/5 已���到��列表页已���到��详情页-www.yrx.com/6 已���到��列表页 已���到��详情页-www.yrx.com/7 已���到��列表页已���到��详情页-www.yrx.com/8 已���到��列表页已���到��详情页-www.yrx.com/9 True 详情页已抓完 True 详情页已抓完

050168991088867å¹¶å�‘æ�§åˆ¶-Semaphore先看下é�¢çš„代ç �:importthreadingimporttimeclassGetHtml(threading.Thread):def__init__(self,url):super().__init__()self.urlurldefrun(self):time.sleep(

print(f��详情页�功:{self.url})classSendUrl(threading.Thread):def__init__(self):super().__init__()defrun(self):foriinrange(

:urlwww.yrx.com/{}.format(i)get_html_threadGetHtml(urlurl)get_html_thread.start()if__name____main__:send_urlSendUrl()send_url.start()�行结�是:...... ��详情页�功: www.yrx.com/9 ��详情页�功: www.yrx.com/11 ��详情页�功: www.yrx.com/12 ��详情页�功: www.yrx.com/19 ��详情页�功: www.yrx.com/23 ��详情页�功: www.yrx.com/22 ��详情页�功: www.yrx.com/28 ��详情页�功: www.yrx.com/29如� for i in range(

: 创建的线程数更多呢, 比如是 30000, 那么代ç �是有问题的, 系统很å�¯èƒ½å› 为ä¸�会创建这么多线程而出ç�°å¼‚常错误, 所以这个时候, 我们就需è¦�使用 Semaphore 对并å�‘数进行æ�§åˆ¶.好的, æ�¥ä¸‹æ�¥æˆ‘们使用 Semaphore æ�¥æ�§åˆ¶å¹¶å�‘.改写代ç �如下:# Semaphore, 用äº�æ�§åˆ¶å¹¶å�‘, é”�.importthreadingimporttimeclassGetHtml(threading.Thread):def__init__(self,url,semaphore):super().__init__()self.urlurl self.semaphoresemaphoredefrun(self):time.sleep(

print(f��详情页�功:{self.url})# 释放�的�置self.semaphore.release()classSendUrl(threading.Thread):def__init__(self,semaphore:threading.Semaphore):super().__init__()self.semaphoresemaphoredefrun(self):foriinrange(

:urlwww.yrx.com/{}.format(i)# åŠ é”�çš„ä½�ç½®self.semaphore.acquire()get_html_threadGetHtml(urlurl,semaphoreself.semaphore)get_html_thread.start()if__name____main__:# 使用 Semaphore æ�¥æ�§åˆ¶å¹¶å�‘æ•°æ¯�次是5个线程semaphorethreading.Semaphore(

send_urlSendUrl(semaphore)send_url.start()使用 threading.Semaphore(

对代ç �改写了之å��, å�³ä½¿åœ¨å¾ªç�¯é‡Œè®¾ç½®äº†30000个线程, æ¯�次并å�‘数都是5个.çº¿ç¨‹æ± å‰�é�¢å·²ç»�说了多线程, 为什么还需è¦�çº¿ç¨‹æ± å‘¢?è¿™æ˜¯å› ä¸ºå¤šçº¿ç¨‹åœ¨ä¸€äº›åœºæ™¯ä¸‹æœ‰ä¸�足的地方, 我们先æ�¥çœ‹çœ‹ 线程的执行顺åº�:调用 start() 新建线程 —— 准备就绪状æ€� —— 等待æ“�作系统的调度, CPU资æº� (消耗资æº�, 系统资æº�, CPU, 内存)é�‡åˆ° IO æ“�作时, GIL会释放, 线程会切æ�¢, è¿™æ ·çš„è¯�å�ˆä¼šæ‰§è¡Œä¸Šé�¢çš„(调用start()新建线程…)调用 run()方法执行完线程里的代ç � (å›�收资æº�)è¿™æ ·ä¸€æ�¥, 创建线程数很多的è¯�, 频ç¹�消耗资æº�ã€�å›�收资æº�导致效ç�‡å¾ˆä½�, 如æ�œæƒ³è¦�é�¿å…�上é�¢çš„问题, å�¯ä»¥çœ‹çœ‹ä¸‹é�¢çº¿ç¨‹æ± çš„å®�ç�°.首先, çº¿ç¨‹æ± çš„å¥½å¤„æ˜¯:æ��å�‡æ€§èƒ½, é‡�用资æº�, 有效地é�¿å…�了创建线程过多.主线程中å�¯ä»¥è�·å�–到æŸ�1个线程的返å›�值, 还å�¯ä»¥è�·å�–线程的è¿�行状æ€�.æ�¥å�£ä¸€è‡´ (线程ã€�进程和å��ç¨‹ä½¿ç”¨äº†ä¸€æ ·çš„è®¾è®¡æ¨¡å¼� .Future), å­¦ä¹ æˆ�本更ä½�, 代ç �维护æˆ�本ä½�.ok, 有了上é�¢çš„æ¦‚念, 我们æ�¥çœ‹çœ‹è¿™æ®µçº¿ç¨‹æ± 的基础代ç �:importtimefromconcurrent.futuresimportThreadPoolExecutordeftask_cost_time(times):time.sleep(times)print(f程åº�花费了{times}ç§’)returntimes executorThreadPoolExecutor(

task1executor.submit(task_cost_time,

task2executor.submit(task_cost_time,

我们直æ�¥è¿�行上é�¢çš„代ç �, 会得到:程åº�花费了2ç§’ 程åº�花费了3秒需è¦�注æ„�的是, submit()方法是里是自动执行start()å�¯åŠ¨çº¿ç¨‹çš„, submit()方法的返å›�值是 Future 对象, 并且 submit()方法是ä¸�阻å¡�çš„. 也就是说, 主线程并ä¸�会 join 等待上é�¢çš„两个线程执行结æ�Ÿæ‰�会执行å��é�¢çš„代ç �.done()我们使用 done()方法æ�¥åˆ¤æ–­çº¿ç¨‹æ˜¯å�¦æ‰§è¡Œå®Œæ¯•:...print(task

done())time.sleep(

3.

print(task

done())�行结�是:False 程�花费了2秒 程�花费了3秒 Trueresult()主线程使用 result()方法 �以��到�一个线程的返�值....print(

print(task

result())print(

�行结�是:111111111 程�花费了2秒 程�花费了3秒 3 222222222我们看到�有执行了 print(task

result()) 得到了返å›�值3, æ‰�打å�° 222222222, 也就是说, result()方法是阻å¡�çš„.cancel()使用 cancel()方法å�¯ä»¥å�–消线程, 但是处äº�正在è¿�行状æ€�或者已完æˆ�的线程, ä¸�å…�许å�–消.我们将å‰�é�¢çš„代ç �改写:...deftask_cost_time(times):time.sleep(times)print(f程åº�花费了{times}ç§’)returntimes executorThreadPoolExecutor(

task1executor.submit(task_cost_time,

task2executor.submit(task_cost_time,

print(task

cancel())print(task

cancel())è¿�行结æ�œæ˜¯:False True 程åº�花费了3秒此时并没有执行 task2, 它被å�–消è¿�行了.我们看一下 cancel()方法的æº�ç �, å�¯ä»¥çœ‹å‡º:处äº�正在è¿�行状æ€�或者已完æˆ�的线程, ä¸�å…�许å�–消.defcancel(self):Cancel the future if possible. Returns True if the future was cancelled, False otherwise. A future cannot be cancelled if it is running or has already completed. withself._condition:ifself._statein[RUNNING,FINISHED]:returnFalseifself._statein[CANCELLED,CANCELLED_AND_NOTIFIED]:returnTrueself._stateCANCELLED self._condition.notify_all()self._invoke_callbacks()returnTrueas_completed()as_completed()方法返å›�的是生æˆ�器(特殊的迭代器).importthreadingimporttimefromconcurrent.futuresimportThreadPoolExecutor,as_completeddeftask_cost_time(times):time.sleep(times)print(f程åº�花费了{times}ç§’)returntimes executorThreadPoolExecutor(

time_list[3,2,4]# tasks: 一个Future的列表.tasks[executor.submit(task_cost_time,t)fortintime_list]# as_completed, fs就是Futuresforfinas_completed(tasks):print(f.result())�行结�是:程�花费了2秒 2 程�花费了3秒 3 程�花费了4秒 4我们�� as_completed()方法 先请求好的先给出结�.map()map()方法和 as_completed()方法的功能很相似, 但是map()方法�是先请求好的先给出结�, 而是按照生�的 Future列表中的Future对象的顺�执行.importthreadingimporttimefromconcurrent.futuresimportThreadPoolExecutordeftask_cost_time(times):time.sleep(times)print(f程�花费了{times}秒)returntimes executorThreadPoolExecutor(

time_list[3,2,4]tasksexecutor.map(task_cost_time,time_list)forfintasks:print(f)è¿�行结æ�œæ˜¯:程åº�花费了2ç§’ 程åº�花费了3ç§’ 3 2 程åº�花费了4ç§’ 4map()方法的æº�ç �是:defmap(self,fn,*iterables,timeoutNone,chunksize

:iftimeoutisnotNone:end_timetimeouttime.monotonic()fs[self.submit(fn,*args)forargsinzip(*iterables)]# Yield must be hidden in closure so that the futures are submitted# before the first iterator value is required.defresult_iterator():try:# reverse to keep finishing orderfs.reverse()whilefs:# Careful not to keep a reference to the popped futureiftimeoutisNone:yieldfs.pop().result()else:yieldfs.pop().result(end_time-time.monotonic())finally:forfutureinfs:future.cancel()returnresult_iterator()其中 fs.reverse() 是将 Future列表中的Future对象å��转过æ�¥, è¿™æ ·åœ¨æ‰§è¡Œå��é�¢çš„ yield fs.pop().result() 代ç �æ—¶, 会按照生æˆ�çš„ Future列表中的Future对象的顺åº�执行(å› ä¸ºå‰�é�¢è¯´è¿‡result()方法是阻å¡�执行的).wait()如æ�œæˆ‘们想è¦�å®�ç�°ç±»ä¼¼äº�之å‰�的代ç �中 join()方法的功能, 比如主线程è¦�等到å­�线程全部执行完毕之å��, æ‰�能执行主线程å��é�¢çš„代ç �.这个时候å�¯ä»¥ä½¿ç”¨ wait()方法:importtimefromconcurrent.futuresimportThreadPoolExecutor,waitdeftask_cost_time(times):time.sleep(times)print(f程åº�花费了{times}ç§’)returntimes executorThreadPoolExecutor(

task1executor.submit(task_cost_time,

task2executor.submit(task_cost_time,

wait([task1,task2])print(

è¿�行结æ�œæ˜¯:程åº�花费了2ç§’ 程åº�花费了3ç§’ 111111我们æ�¥çœ‹çœ‹ wait()方法的定义:defwait(fs,timeoutNone,return_whenALL_COMPLETED):缺çœ�情况下 return_when的值是ALL_COMPLETED, 也就是在所有的线程都执行完毕å��æ‰�è¿”å›�.如æ�œæƒ³è¦�å®�ç�°å®Œæˆ�了一个线程就返å›�, å�ªéœ€è¦�è¿™æ ·å®�ç�°:...executorThreadPoolExecutor(

task1executor.submit(task_cost_time,

task2executor.submit(task_cost_time,

wait([task1,task2],return_whenFIRST_COMPLETED)print(

è¿�行结æ�œæ˜¯:程åº�花费了2ç§’ 111111 程åº�花费了3ç§’wait([task1, task2], return_whenFIRST_COMPLETED) 这个时候å�ªè¦�完æˆ�了一个线程就会返å›�.with语å�¥ä¹‹å‰�有在Python魔法方法一文中的__enter__å’Œ__exit__方法里讲到这å�—的知识, è€�é“�们å�¯ä»¥å›�顾一下:31天Python入门——第20天:魔法方法详解with语å�¥, 上下文管ç�†å™¨.当一个类里定义了 enter方法和exit方法, 那么这个类就å�¯ä»¥è¢«å�«å�šä¸Šä¸‹æ–‡ç®¡ç�†å™¨, 它é�µå¾ªPython中的上下文管ç�†å™¨å��è®®.__enter__(self)方法在进入with语å�¥ä»£ç �å�—时被调用, 用äº�执行一些准备æ“�作.å�¯ä»¥è¿”å›�一个值, 该值将被赋给as关键字之å��çš„å�˜é‡�.__exit__(self, exc_type, exc_value, exc_traceback)方法在退出with语å�¥ä»£ç �å�—时调用, æ— è®ºä»£ç �å�—是å�¦å�‘生异常.å�¯ä»¥ç”¨äº�执行一些清ç�†æ“�作, 如资æº�的释放.如æ�œä»£ç �å�—内没有异常å�‘生, å�‚数值为 None, None, None.如æ�œæœ‰å¼‚常, å�‚数包å�«å¼‚常类å�‹ã€�异常å®�例和跟踪信æ�¯.如æ�œ__exit__方法返å›� True, 异常ä¸�会å�‘ä¸Šç»§ç»­ä¼ æ’­.è¿”å›� False 则异常会继续å�‘上抛.比如这段代ç �:classMyRequest:def__enter__(self):print(

returnselfdef__exit__(self,exc_type,exc_val,exc_tb):passwithMyRequest()asmr:print(mr)è¿�行结æ�œæ˜¯:11111111 __main__.MyRequest object at 0x7faed014c160也就是:在进入with语å�¥ä»£ç �å�—时执行enter()方法, 打å�°11111111, å¹¶è¿”å›�了å®�例对象, 而且这个返å›�值会被赋给as关键字之å��çš„å�˜é‡�mr.如æ�œwith语å�¥ä»£ç �å�—中有异常错误:classMyRequest:def__enter__(self):returnselfdef__exit__(self,exc_type,exc_val,exc_tb):print(

# return TruewithMyRequest()asmr:1/0è¿�行结æ�œæ˜¯:2222222 ZeroDivisionError: division by zero如æ�œä¸�想å†�å�‘上抛出异常, å�ªéœ€è¦�在 exit()方法中返å›�True.é�‡è§�安然é�‡è§�ä½ ä¸�负代ç �ä¸�è´Ÿå�¿ã€‚谢谢è€�é“�的时间咱们下篇å†�è§�

9·1免费版网站下载-9·1免费版网站下载应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123