Python with原理,python中with的用法
python中with的用法和原理(上下文管理器)在Python _ Technical Blog of _ MB 62 AFB 3 AFB 54 FB _ 51 CTO Blog中from contextlib导入关闭的用法
python中with的用法和原理(上下文管理器)在Python中使用from contextlib导入关闭
python (Context Manager)中with的用法和原理preamble with语句适用于访问资源,确保将执行必要的“清理”操作来释放资源,如使用后自动关闭文件/线程中锁的自动获取和释放等。
这个问题导致了下面的代码:
文件=打开( 1.txt )
data=file.read()
file.close()上面的代码有两个问题:
文件读取出现异常,但没有处理;
您可能忘记关闭文件句柄;
改善尝试:
f=打开( xxx )
除了:
打印(“无法打开”)
退出(-1)
尝试:
做某事
除了:
做某事
最后:
F.close()虽然这段代码运行良好,但是相当冗长。
使用with语句可以减少冗长性,并自动处理由上下文生成的异常。例如下面的代码:
打开( 1.txt )作为文件:
data=file.read()with 工作原理with后面的语句求值后,调用返回对象的__enter__方法,返回值将赋给as后面的变量;
当with语句体全部执行完毕后,会调用之前返回对象的__exit__方法。
用工作原理代码举例:
班级样本:
def __enter__(自身):
打印( in __enter__ )
返回“Foo”
def __exit__(self,exc_type,exc_val,exc_tb):
打印( in __exit__ )
def get_sample():
返回样本()
以get_sample()为样本:
打印(样本: ,样本)运行结果:
整个操作过程如下:
(1)执行_ _ enter _ _方法;
(2)将_ _ enter _ _方法的返回值,在本例中为“Foo”,赋给变量sample;
(3)执行代码块,将样本变量的值打印为“foo”;
(4)调用_ _ exit _ _方法;
[注意exit _ _方法中有三个参数,exc_type、exc_val和exc_tb。这些参数在异常处理中非常有用。
参数说明:exc_type:类型错误。
Exc_val:对应于错误类型的值
Exc_tb:代码中出现错误的位置。
示例代码:
班级样本:
def __enter__(自身):
打印(“输入”)
回归自我
def __exit__(self,exc_type,exc_val,exc_tb):
print(type:,exc_type)
打印( val:,exc_val
打印( tb:,exc_tb)
def do_something(自己):
bar=1/0
返回栏10
以样品()为样品:
sample.do_something()的运行结果:
综上所述,其实当with之后的代码块抛出异常时,执行的是__exit__方法。开发库时,可以将清理资源和关闭文件等操作放在__exit__方法中。
总之,with-as表达式大大简化了每次写finally的工作,对代码的优雅有很大的帮助。
只要实现两个方法__enter__()和__exit__()的类可以轻松创建上下文管理器,就可以使用With。
如果有多个条目,可以这样写:
用open(1.txt )作为f1,open(2.txt )作为f2:
用语句上下文管理协议做点什么的原理:它包括方法__enter__()和__exit__(),支持这个协议的对象要实现这两个方法。上下文管理器:支持上下文管理协议并实现__enter__()和__exit__()方法的对象。上下文管理器定义了在执行with语句时要建立的运行时上下文,并负责在with语句块的上下文中执行进入和退出操作。上下文管理器通常用语句调用,也可以通过直接调用其方法来使用。语句的常用表达式:
以EXPR为VAR: # EXPR可以是任何表达式。
BLOCK的一般执行过程如下:
1.执行EXPR以生成context _ manager上下文_管理器;
2.获取上下文管理器的__exit()__方法,并保存它以供以后调用;
3.调用上下文管理器的__enter__()方法;如果使用as子句,将__enter__()方法的返回值赋给VAR在as子句中;
4.分块执行表达式;
5.无论执行过程中是否出现异常,执行上下文管理器的__exit__()方法。__exit__()方法负责“清理”工作,比如释放资源。
如果执行过程中没有异常,或者在with语句体中执行了语句break/continue/return,则调用_ _ exit _ _ (none,None,None),参数为None;如果执行过程中发生异常,sys.exc_info获取的异常信息用于调用_ _ exit _ _ (exc _ type,exc _ value,exc _ traceback)作为参数;
6.当异常发生时,如果__exit__(type,value,traceback)返回False,将再次抛出异常,由with以外的语句逻辑处理异常,这也是常见的做法;如果返回True,异常将被忽略,并且不会处理任何异常。
自定义上下文管理器python的with语句是为了提供一种有效的机制,使代码更加简洁,同时在产生异常时更容易清理。
示例1:
DBManager类(对象):
def __init__(self):
及格
def __enter__(自身):
打印( __enter__ )
回归自我
def __exit__(self,exc_type,exc_val,exc_tb):
打印( __退出_ _ )
返回True
def getInstance():
返回数据库管理器()
使用getInstance()作为dbManagerIns:
Print(with demo) [Note] With后面必须跟一个上下文管理器。如果使用as,则将上下文管理器的__enter__方法的返回值赋给target,可以是单个变量,也可以是用“()”括起来的元组【不能是用“,”分隔的变量列表,但必须加上“()”】
运行结果:
分析:当我们使用with语句时,调用__enter__方法,将__enter__方法的返回值赋给as后的变量,当我们用with退出时,自动执行__exit__方法。
示例2:
带有_work(对象)的类:
def __enter__(自身):
在使用语句“”输入时调用“”
打印(回车调用)
Return 打印对象f的值
def __exit__(self,exc_type,exc_val,exc_tb):
当与“”一起离开时,与调用了“”
打印(调用退出)
With_work()为f:
打印(f)
Print(用代码块打印输出)
Print (执行代码块后打印with )运行结果:
[注意]不实现两个方法__enter__()和__exit__()的类不能创建上下文管理器,也不能使用with语句。
例如:
类门(对象):
定义打开(自身):
打印(“门已打开”)
定义关闭(自己):
打印(“门已关闭”)
以门()为d:
D.open()运行结果:
在python中使用from contextlib导入关闭官方:https://docs.python.org/dev/library/contextlib.html
1.python中的一些类没有实现__enter__()和__exit__()这两个方法。您也可以使用with语句。但前提是实现了close()语句。例如:
导入上下文库
类门(对象):
定义打开(自身):
打印(“门已打开”)
定义关闭(自己):
打印(“门已关闭”)
使用contextlib.closing(Door())作为门:
door.open()的运行结果:
2.contextlib.closing(xxx)的原理如下:类关闭(object):
“”上下文在块的末尾自动关闭某些内容。
像这样的代码:
带关闭(模块。open( arguments))作为f:
街区
相当于这样:
f=模块。打开(参数)
尝试:
街区
最后:
f.close()
def __init__(self,thing):
自我。事物=事物
def __enter__(自身):
回归自我
def __exit__(self,*exc_info):
self . thing . close()context lib . closing()会自动在某些类中添加__enter__()和__exit__()两个方法,使其满足上下文管理器的条件。
3、并不是只有类才能满足上下文管理器的条件,其实方法也可以实现一个上下文管理器【上下文库。上下文管理器】可以通过@contextlib.contextmanager装饰器的方式实现,但是其装饰的方法必须是一个生成器百分之十的产量关键字前半段用来表示__输入_ _()方法,产量关键字后半段用来表示__exit__()方法。
例如:
导入上下文库
@contextlib.contextmanager
定义标签(名称):
打印( %s %名称)
产量
打印(/%s % name )
带有标签(名称=h1 ):
print(hello world!)运行结果:
4、使用contextlib.contextmanager实现装饰器才能做的事情例如:比如给一段代码加时间花费计算。
普通装饰器版本:【此方法解决此类需求要更加方便美观】导入时间
定义包装器(函数):
def new_func(*args,**kwargs):
t1=time.time()
ret=func(*args,**kwargs)
t2=time.time()
打印(成本时间=,(t2 - t1))
返回浸水使柔软
返回新函数
@包装
def hello(a,b):
时间。睡眠(1)
打印( a b=,a b)
if __name__==__main__ :
你好(100,200)运行结果:
contextlib.contextmanger版本:导入时间
导入上下文库
@contextlib.contextmanager
定义成本时间():
t1=time.time()
产量
t2=time.time()
打印(成本时间=,t2 - t1)
用成本时间():
时间。睡眠(1)
a=100
b=200
打印( a b=,a b)原理:
1、因为成本时间()方法是个生成器,所以运行__输入_ _()的时候,上下文管理器调用self.gen.next()会跑到成本时间()方法的产量处,停住挂起,这个时候已经有了t1=时间。time();
2、然后运行随着语句体里面的语句,也就是a b=300
3、跑完后运行__exit__()的时候,上下文管理器调用self.gen.next()会从成本时间()的产量的下一句开始一直到结束。这个时候有了t2=time.time(),t2-t1从而实现了统计成本_时间的效果。
5、上下文库源码如下:导入系统
类GeneratorContextManager(对象):
" @contextmanager decorator的助手"
def __init__(self,gen):
self.gen=gen
def __enter__(自身):
尝试:
return self.gen.next()
除了停止迭代:
引发RuntimeError("生成器未生成")
def __exit__(自身,类型,值,追溯):
如果类型为无:
尝试:
self.gen.next()
除了停止迭代:
返回
否则:
引发RuntimeError("生成器没有停止")
否则:
如果值为无:
#需要强制实例化,以便我们能够可靠地
#告诉我们是否得到相同的异常
值=类型()
尝试:
self.gen.throw(类型、值、回溯)
引发运行时错误( throw()后生成器没有停止)
除了停止迭代作为exc:
返回宝立来电池不是值
除了:
如果sys.exc_info()[1]不是值:
上升
定义上下文管理器(函数):
@wraps(func)
def helper(*args,**kwds):
返回GeneratorContextManager(func(* args,**kwds))
返回助手
去期待陌生,去拥抱惊喜。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。