### 讀寫文件資源注意關閉
#### 使用 try...finally 關閉
在Python中,讀寫文件這樣的資源要特別注意,必須在使用完畢后正確關閉它們。正確關閉文件資源的一個方法是使用`try...finally`:
~~~
try:
f = open('/path/to/file', 'r')
f.read()
finally:
if f:
f.close()
~~~
#### 使用 with 關閉
寫`try...finally`非常繁瑣。Python的`with`語句允許我們非常方便地使用資源,而不必擔心資源沒有關閉,所以上面的代碼可以簡化為:
~~~
with open('/path/to/file', 'r') as f:
f.read()
~~~
并不是只有`open()`函數返回的`fp`對象才能使用`with`語句。實際上,任何對象,只要正確實現了上下文管理,就可以用于`with`語句。
### 實現上下文管理
實現上下文管理是通過`__enter__`和`__exit__`這兩個方法實現的。例如,下面的`class`實現了這兩個方法:
~~~
class Query(object):
def __init__(self, name):
self.name = name
def __enter__(self):
print('Begin')
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
print('Error')
else:
print('End')
def query(self):
print('Query info about %s...' % self.name)
~~~
這樣我們就可以把自己寫的資源對象用于`with`語句:
~~~
with Query('Bob') as q:
q.query()
@contextmanager
~~~
編寫`__enter__`和`__exit__`仍然很繁瑣,因此Python的標準庫`contextlib`提供了更簡單的寫法,上面的代碼可以改寫如下:
~~~
from contextlib import contextmanager
class Query(object):
def __init__(self, name):
self.name = name
def query(self):
print('Query info about %s...' % self.name)
@contextmanager
def create_query(name):
print('Begin')
q = Query(name)
yield q
print('End')
~~~
`@contextmanager`這個`decorator`接受一個`generator`,用`yield`語句把`with ... as var`把變量輸出出去,然后,`with`語句就可以正常地工作了:
~~~
with create_query('Bob') as q:
q.query()
~~~
很多時候,我們希望在某段代碼執行前后自動執行特定代碼,也可以用`@contextmanager`實現。例如:
~~~
@contextmanager
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name)
with tag("h1"):
print("hello")
print("world")
上述代碼執行結果為:
<h1>
hello
world
</h1>
~~~
代碼的執行順序是:
`with`語句首先執行`yield`之前的語句,因此打印出`<h1>`;
`yield`調用會執行`with`語句內部的所有語句,因此打印出`hello`和`world`;
最后執行`yield`之后的語句,打印出`</h1>`。
因此,`@contextmanager`讓我們通過編寫`generator`來簡化上下文管理。
### @closing
如果一個對象沒有實現上下文,我們就不能把它用于`with`語句。這個時候,可以用`closing()`來把該對象變為上下文對象。例如,用`with`語句使用`urlopen()`:
~~~
from contextlib import closing
from urllib.request import urlopen
with closing(urlopen('https://www.python.org')) as page:
for line in page:
print(line)
~~~
`closing`也是一個經過`@contextmanager`裝飾的`generator`,這個`generator`編寫起來其實非常簡單:
~~~
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()
~~~
它的作用就是把任意對象變為上下文對象,并支持`with`語句。
`@contextlib`還有一些其他`decorator`,便于我們編寫更簡潔的代碼。
- Python教程
- Python簡介
- 安裝Python
- Python解釋器
- 第一個 Python 程序
- 使用文本編輯器
- Python代碼運行助手
- 輸入和輸出
- 源碼
- learning.py
- Python基礎
- 數據類型和變量
- 字符串和編碼
- 使用list和tuple
- 條件判斷
- 循環
- 使用dict和set
- 函數
- 調用函數
- 定義函數
- 函數的參數
- 遞歸函數
- 高級特性
- 切片
- 迭代
- 列表生成式
- 生成器
- 迭代器
- 函數式編程
- 高階函數
- map/reduce
- filter
- sorted
- 返回函數
- 匿名函數
- 裝飾器
- 偏函數
- Python函數式編程——偏函數(來自博客)
- 模塊
- 使用模塊
- 安裝第三方模塊
- 面向對象編程
- 類和實例
- 訪問限制
- 繼承和多態
- 獲取對象信息
- 實例屬性和類屬性
- 面向對象高級編程
- 使用__slots__
- 使用@property
- 多重繼承
- 定制類
- 使用枚舉類
- 使用元類
- 錯誤、調試和測試
- 錯誤處理
- 調試
- 單元測試
- 文檔測試
- IO編程
- 文件讀寫
- StringIO和BytesIO
- 操作文件和目錄
- 序列化
- 進程和線程
- 多進程
- 多線程
- ThreadLocal
- 進程 vs. 線程
- 分布式進程
- 正則表達式
- 常用內建模塊
- datetime
- collections
- base64
- struct
- hashlib
- itertools
- contextlib
- XML
- HTMLParser
- urllib
- 常用第三方模塊
- PIL
- virtualenv
- 圖形界面
- 網絡編程
- TCP/IP簡介
- TCP編程
- UDP編程
- 電子郵件
- SMTP發送郵件
- POP3收取郵件
- 訪問數據庫
- 使用SQLite
- 使用MySQL
- 使用SQLAlchemy
- Web開發
- HTTP協議簡介
- HTML簡介
- WSGI接口
- 使用Web框架
- 使用模板
- 異步IO
- 協程
- asyncio
- async/await
- aiohttp
- 實戰
- Day 1 - 搭建開發環境
- Day 2 - 編寫Web App骨架
- Day 3 - 編寫ORM
- Day 4 - 編寫Model
- Day 5 - 編寫Web框架
- Day 6 - 編寫配置文件
- Day 7 - 編寫MVC
- Day 8 - 構建前端
- Day 9 - 編寫API
- Day 10 - 用戶注冊和登錄
- Day 11 - 編寫日志創建頁
- Day 12 - 編寫日志列表頁
- Day 13 - 提升開發效率
- Day 14 - 完成Web App
- Day 15 - 部署Web App
- Day 16 - 編寫移動App
- FAQ
- 期末總結