1.什么是上下文管理器
- 先來說一下什么是上下文管理器吧迅皇,其實上下文管理器就是一個包裝任意代碼塊的對象榴徐,上下文管理器保證進入上下文管理器時技掏,每次代碼執(zhí)行的一致性署恍,當(dāng)退出上下文管理器時隔显,相關(guān)的資源會被正確的回收却妨。使用上下文管理器的好處是,一定能夠保證退出步驟的執(zhí)行括眠,通過 with 語句在編寫代碼時彪标,會使代碼變得更加簡潔,不用再去關(guān)閉文件
2.如何實現(xiàn)上下文管理器
我們通過實例來看一下如何實現(xiàn)上下文管理器:
class Student:
def __init__(self): # 第一步:首先進入上下文之前會調(diào)用初始化方法
print('init')
self.name = '張三'
self.age = 27
def __enter__(self): # 第二步:然后進入上下文會調(diào)用__enter__方法掷豺,return返回的結(jié)果會通過as關(guān)鍵字賦值給后面的變量
print('enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit') # 第四步:執(zhí)行完畢語句塊后捞烟,會調(diào)用__exit__方法退出上下文
with Student() as stu:
print(stu) # 第三步:調(diào)用了__enter__后,會執(zhí)行上下文的語句塊
實現(xiàn)上下文管理器有兩種方式当船,一個是通過類實現(xiàn)
__enter__
和__exit__
方法题画,也就是通過with語句進行上下文管理;還有一種方法是通過contextlib模塊裝飾器和生成器實現(xiàn)上下文管理生年;
with 語句實現(xiàn)的上下文管理
class Student:
def __init__(self):
print('init')
self.name = '張三'
self.age = 27
def __enter__(self):
print('enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit')
# 即使在上下文語句中出現(xiàn)異常婴程,那么退出上下文的時候,也會執(zhí)行__exit__方法
with Student() as stu:
raise Exception('自定義拋出一個異常')
# 數(shù)據(jù)庫連接
class Data:
def __init__(self):
self.influxdb_client_1 = InfluxDBClient(host='1.1.1.1', port=8086, database='database1', )
self.influxdb_client_2 = InfluxDBClient(host='2.2.2.2', port=8086, database='database1', )
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.influxdb_client_1.close()
self.influxdb_client_2.close()
def get_metrics(self, server_name):
print('從influxdb中查詢到所有的metrics')
return '將所有的metrics返回'
# 使用上下文管理查詢數(shù)據(jù)的過程, 即使中間拋出異常抱婉,也會執(zhí)行__exit__方法档叔,關(guān)閉數(shù)據(jù)庫連接
with Data() as data:
data.get_metrics(server1)
with語句的語法為:
with 表達(dá)式 [as 目標(biāo)]:代碼塊
桌粉;當(dāng)with遇到上下文管理器,就會在執(zhí)行語句體之前衙四,先執(zhí)行上下文管理器的
__enter__
方法铃肯,然后再執(zhí)行語句體,執(zhí)行完語句體后传蹈,最后執(zhí)行__exit__
方法押逼;with語句支持嵌套,支持多個with子句惦界,它們兩者可以相互轉(zhuǎn)換挑格;
contextlib模塊實現(xiàn)上下文管理器
# 讓普通函數(shù)也可以使用上下文管理, 前提條件是add函數(shù)必須是生成器函數(shù)碌燕,且只有yield語句
from contextlib import contextmanager
@contextmanager
def add(x, y):
print('__enter__') # 第二部:調(diào)用__enter__
yield x + y # 第二部:返回__enter__函數(shù)的返回值給obj對象
print('__exit__') # 第四部:調(diào)用__exit__
with add(1,2) as obj:
print(obj) # 第三部:執(zhí)行上下文語句塊
contextlib模塊是Python標(biāo)準(zhǔn)庫提供的更加易用的上下文管理器工具模塊锦担;
它是通過裝飾器實現(xiàn)的,不需要再創(chuàng)建類以及使用
__enter__
和__exit__
這兩個方法坊饶,比with語句更加方便灾搏;@contextmanager是一個裝飾器decorator挫望,它接收一個生成器generator,把生成器里yield的值賦給
with...as
后的變量狂窑,然后正常執(zhí)行with語句媳板;closing 也是一個經(jīng)過 @contextmanager 裝飾的裝飾器,closing( )可以把對象變?yōu)樯舷挛膶ο笕缓笫褂脀ith語句(前提是這個對象能調(diào)用close( )方法r刃摇);
3.functools.total_ordering裝飾器
使用functools.total_ordering裝飾器可以很方便讓實例比較大兄枷铩:
from functools import total_ordering
@total_ordering
class Student:
def __init__(self, price):
self.__price = price
@property
def price(self):
return self.__price
# 如果需要比較大小巨缘,必須實現(xiàn)'< > <= >='一個其中的一個
def __lt__(self, other):
return self.__price < other.__price
stu1 = Student(10)
stu2 = Student(19)
print(stu1 < stu2)
print(stu1 > stu2)
# 輸出:
True
False
- functools.total_ordering裝飾器是在python2.7的時候加上的,它是針對某個類如果定義了
__lt__
采呐、__le__
、__gt__
搁骑、__ge__
這些方法中的至少一個斧吐,使用該裝飾器,則會自動的把其他幾個比較函數(shù)也實現(xiàn)在該類中
例如上面的實例仲器,我們只定義了__lt__()
方法煤率,本來如果我們使用,但是卻能使用__gt__()
方法是會報錯的乏冀,但是用了functools.total_ordering裝飾器之后卻能使用__gt__()
方法了蝶糯,是不是很方便;