直接上代碼:
import requests import csv from contextlib import closing # 保存csv文件 def save_csv(f_name, data): # 1. 創建文件對象 f = open(f_name, 'w', encoding='utf-8', newline='') # 2. 基於文件對象構建 csv寫入對象 csv_writer = csv.writer(f) # 4. 寫入csv文件內容 for row in data: csv_writer.writerow(row) # 5. 關閉文件 f.close() # 下載csv文件 def get_pag(): url = '********' # 讀取數據 with closing(requests.get(url, stream=True)) as r: f = (line.decode('utf-8') for line in r.iter_lines()) reader = csv.reader(f, delimiter=',', quotechar='"') save_csv(f_name, reader) if __name__ == '__main__': f_name = '123.csv' reader = get_pag()
亮點來了:
contextlib.closing 這個庫,學習了.
with xxx as conn1:
with yyy as conn2:
code
真是蠢如老狗呀,其實可以:
with xxx as conn1, yyy as conn2:
code
from contextlib import closing from urllib2 import urlopen with closing(urlopen('http://www.python.org';)) as page: for line in page: print(line)
先來否定我的想法,凡用with就萬事大吉,自動幫我關閉。
class Door(object): def open(self): print 'Door is opened' def close(self): print 'Door is closed' with Door() as d: d.open()
結果:
# 報錯: Traceback (most recent call last): File "1.py", line 38, in <module> with Door() as d: AttributeError: __exit__
class Door(object): def open(self): print 'Door is opened' def close(self): print 'Door is closed' with contextlib.closing(Door()) as door: door.open()
結果:
Door is opened Door is closed
contextlib.closing(xxx),原理如下:
class closing(object): """Context to automatically close something at the end of a block. Code like this: with closing(<module>.open(<arguments>)) as f: <block> is equivalent to this: f = <module>.open(<arguments>) try: <block> finally: f.close() """ def __init__(self, thing): self.thing = thing def __enter__(self): return self.thing def __exit__(self, *exc_info): self.thing.close()
這個contextlib.closing()會幫它加上__enter__()和__exit__(),使其滿足with的條件。
5、是不是只有類才能享受with的便利呀? 我單單一個方法行不行?
行!既然認識了contextlib.closing(),必須認識下contextlib.contextmanager
這是一個裝飾器,可以讓一個func()變成一個滿足with條件的類實例…
!!!這個func()必須是生成器…
yield前半段用來表示__enter__()
yield后半段用來表示__exit__()
from contextlib import contextmanager @contextmanager def tag(name): print("<%s>" % name) yield print("</%s>" % name) with tag("h1"): print 'hello world!'
結果:
<h1>
hello world!
</h1>
import time def wrapper(func): def new_func(*args, **kwargs): t1 = time.time() ret = func(*args, **kwargs) t2 = time.time() print 'cost time=', (t2-t1) return ret return new_func @wrapper def hello(a,b): time.sleep(1) print 'a + b = ', a+b hello(100,200)
結果:
a + b = 300
cost time= 1.00243401527
contextmanger版本:
from contextlib import contextmanager @contextmanager def cost_time(): t1 = time.time() yield t2 = time.time() print 'cost time=',t2-t1 with cost_time(): time.sleep(1) a = 100 b = 200 print 'a + b = ', a + b
結果:
a + b = 300
cost time= 1.00032901764
當然還是用裝飾器方便美觀點啦~
這是contextmanager原理:
1、因為func()已經是個生成器了嘛,所以運行__enter__()的時候,contextmanager調用self.gen.next()會跑到func的yield處,停住掛起,這個時候已經有了t1=time.time()
2、然后運行with語句體里面的語句,也就是a+b=300
3、跑完后運行__exit__()的時候,contextmanager調用self.gen.next()會從func的yield的下一句開始一直到結束。這個時候有了t2=time.time(),t2-t1從而實現了統計cost_time的效果,完美。
源碼:
class GeneratorContextManager(object): """Helper for @contextmanager decorator.""" def __init__(self, gen): self.gen = gen def __enter__(self): try: return self.gen.next() except StopIteration: raise RuntimeError("generator didn't yield") def __exit__(self, type, value, traceback): if type is None: try: self.gen.next() except StopIteration: return else: raise RuntimeError("generator didn't stop") else: if value is None: # Need to force instantiation so we can reliably # tell if we get the same exception back value = type() try: self.gen.throw(type, value, traceback) raise RuntimeError("generator didn't stop after throw()") except StopIteration, exc: return exc is not value except: if sys.exc_info()[1] is not value: raise def contextmanager(func): @wraps(func) def helper(*args, **kwds): return GeneratorContextManager(func(*args, **kwds)) return helper
感謝博主 ouyangbro 的博客 https://blog.csdn.net/emaste_r/article/details/78105713
感謝博主 大蛇王 的博客 https://blog.csdn.net/t8116189520/article/details/103408560