首页 > 编程语言 >Python | with关键字详解

Python | with关键字详解

时间:2023-07-02 20:58:40浏览次数:58  
标签:__ 文件 Python try 关键字 详解 exit file enter

with使用背景

对于系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(释放)该资源。

比如 Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,如果不关闭会出现什么情况呢?极端情况下会出现 Too many open files 的错误,因为系统允许你打开的最大文件数量是有限的。同样,对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 Can not connect to MySQL server Too many connections,因为数据库连接是一种非常昂贵的资源,不可能无限制的被创建。

我们所熟知的try except finally 可以解决上述问题,但这样的结构一多会导致整体代码结构 很臃肿繁琐,不易读、不美观。所以用with…as…语句对其进行简化

例如:文件操作中的with

回想一下文件操作,一般文件操作都会面临一个问题:由于读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确的关闭文件,我们先使用try… finally来实现。

使用try … finally:使用这种方式,无论如何最后文件都会关闭,解决了上述问题,但是代码比较繁琐,我们思考有没有简单的方式。

try:
	# 1.以读的方式打开文件
	f = open('1.txt','r')
	# 2.进行文件操作
	f.write('xxxxx') # 这一步会引发异常

except IOError as e:
	print("文件操作出错",e)

finally:
	f.close()

使用这种方式,无论如何最后文件都会关闭,解决了上述问题,但是代码比较繁琐,我们思考有没有简单的方式。

使用 with 关键字:这种方式,当文件发生异常时,文件将自动关闭。将f贴给open的返回值,当离开with代码块时,系统可以自动调用f.close()方法,那么它的实现原理是什么值得研究?为了研究这种方式,先需要学习什么是上下文管理器。

try:
	# 1.以读的方式打开文件
	with open('1.txt','r') as f
	# 2.进行文件操作
	f.write('xxxxx') # 这一步会引发异常,但是由于使用的with,文件会自动关闭,不会捕获异常

except IOError as e: # useless
	print("文件操作出错",e)

with使用方法

with as 语句的结构如下:

with expression [as variable]:
with-block
例:

with open(file, "w") as f:
    f.write("hello python")

open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。

with详解

with是一个控制流语句,跟if/for/while/try之类的是一类的,with可以用来简化try finally代码,看起来可以比try finally更清晰。

这里新引入了一个"上下文管理协议context management protocol,实现方法是为一个类定义 __enter____exit__两个函数。

首先先要理解上下文。这个要怎么理解呢,很抽象,拿文件操作举例,打开文件就相当于上文,操作文件就相当于文中,关闭文件就相当于下文

with expresion as variable的执行过程是,首先执行__enter__函数,它的返回值会赋给as后面的variable,想让它返回什么就返回什么,只要你知道怎么处理就可以了,如果不写as variable,返回值会被忽略。
然后,开始执行with-block中的语句,不论成功失败(比如发生异常、错误,设置sys.exit(),在with-block执行完成后会执行
exit 函数。

这样的过程其实等价于:

try:
	执行_enter_的内容
	执行 with_block.
finally:
	执行_exit_内容

只不过,现在把一部分代码封装成了 __enter__函数,清理代码封装成 __exit__函数。

with的执行顺序

首先执行expression里面的__enter__函数,它的返回值会赋给as后面的variable,想让它返回什么就返回什么,只要你知道怎么处理就可以了,如果不写as variable,返回值会被忽略。

然后,开始执行with-block中的语句,不论成功失败(比如发生异常、错误,设置sys.exit()),在with-block执行完成后,会执行expression中的__exit__函数。

当with…as语句中with-block被执行或者终止后,这个类对象应该做什么。如果这个码块执行成功,则exception_type,exception_val, trace的输入值都是null。如果码块出错了,就会变成像try/except/finally语句一样,exception_type, exception_val, trace 这三个值系统会分配值。

自定义上下文管理器

class MyFile():
	# 1. __init__()  初始化方法
	def __init__(self,file_name,file_mode):
		self.file_name = file_name
		self.file_mode = file_mode
		
	# 2. __enter__() 上文方法
	def __enter__(self):
		print("进入上文")
		self.file = open(self.file_name,self.file_mode)
		return self.file
	
	# 3. __exit__() 下文方法
	def __exit__():
		print("进入下文")
		self.file.close()

# hello.txt为自己定义的文件
# file 并不是MyFile的一个对象,而是上文__enter__函数返回的内容,是一种资源

with MyFile('hello.txt','r') as file:
	file_data = file.read()
	print(file_data)

# result

进入上文
这里是你文件里的内容
进入下文

使用装饰器实现管理上下文

通过装饰器@contextmanager实现上下文管理:

  • 导入模块:from contextlib import contextmanager

  • @contextmanager
    def myopen(file_name,file_mode):
    	…
    
from contextlib import contextmanager

@contextmanager
def myopen(file_name,file_mode):
	print("进入上文")
	# 1.打开资源
	file = open(file_name,file_mode)
	# 2.返回资源
	yield file
	print("进入下文")
	# 3.关闭资源
	file.close()

with myopen('hello.txt','r') as file:
	file_data = file.read()
	print(file_data)

装饰器contextmanager的作用是什么,他就是把enter和exit函数装饰给了myopen函数,使得文件资源得以自动打开退出(如果你进入该装饰器的源码可以看到里边实现了enter和exit函数)。

标签:__,文件,Python,try,关键字,详解,exit,file,enter
From: https://www.cnblogs.com/zhangxuegold/p/17521351.html

相关文章

  • Angular Component 里 get 关键字修饰的属性的用法
    在Angular中,get关键字用于定义一个访问器属性(accessorproperty),它是一种特殊的属性,可以通过在类中定义一个带有get关键字的方法来实现。当访问这个属性时,会调用这个get方法,并返回该方法的返回值。这种方法使得访问属性时可以执行一些自定义操作,例如计算属性值、验证数据或......
  • 对extern关键字的理解
    对extern关键字的理解(51条消息)对于C++中的extern关键字用法的理解_c++extern_Fanfan21ya的博客-CSDN博客补充:extern语句如果包含初始值就不是声明,而变成了定义:externinta=10; //定义extern不会给变量分配存储空间......
  • python: multiple inheritance
    多继承"""Mother.py多继承类inheritedAnimalpython(类名)superjava继承可以使用extends和implements这两个关键字来实现继承C++:public类名C#:类名可以重写父类方法edit:geovindu,GeovinDudate:20230702IDE:PyCharm2023.1.2"""importsysimportosi......
  • python中globals()的用法
    python中globals()的用法 1.获取所有的全局变量,获取到的内容如下:{'__name__':'__main__','__doc__':None,'__package__':None,'__loader__':<_frozen_importlib_external.SourceFileLoaderobjectat0x7efc4bd1d960>,�......
  • Python入门
    一、逻辑运算符的一些记录#非布尔值的与或运算#当我们对非布尔值进行与或运算时,Python会将其当做布尔值运算,最终会返回原值#与运算的规则#与运算是找False的,如果第一个值是False,则不看第二个值#如果第一个值是False,则直接返回第一个值,否则返回第二个值#或......
  • sftp命令详解
    Lookslikesftpdoesn't distinguishBinaryfilesandASCIIfilesatall.Thatmeansitdoesntsupportthecommandslike'bin'or'ascii'thatftpsupports.sftp>helpAvailablecommands:byeQui......
  • Python - 编写Unicode 字符串
    Python的字符串字面量支持"\xNN"十六进制字节值转义以及"\uNNNN"和"\UNNNNNNNN"Unicode转义。第一种形式用4位十六进制数编码2字节(16位)位字符码点第二种形式用8位十六进制数编码4字节(32位)码点。十六进制值0xCD和0xE8,是ASCII的7位字符范围之外的两个特殊的声调字符......
  • Python 满足列中任意两个数之和等于目标值,输出这两个数的值和所在列表的索引值
    给定一个列表为nums=[2,7,11,15],目标值target=9,找出列表中任意2数之和等于9的元素以及所在位置思路:双重遍历去一对一的比较判断1nums=[2,7,11,15,1,8,2]2target=93list_new=[]4deffind_num_indx():56foriinrange(len(nums)):......
  • python 字典key单引号变双引号
    背景:str1="{'a':1,'b':2,'c':3}"把字典格式的字符串str1转成字典importjsons_dic=json.loads(str1)报错信息:json.decoder.JSONDecodeError:Expectingpropertynameenclosedindoublequotes:line1column2(char1) 解决思路:方法一:(不建......
  • Python 找出一个字符串出现3次的元素
    方式一:利用字典中key唯一来判断存在时,计数统计给value,不存在时,赋值value为1A='aabbcccdddd'B={}foriinA:ifiinB:#直接判断key在不在字典中B[i]+=1else:B[i]=1print(B)print(B) 方式二:用count去统计......