理解 Python 中的语句和上下文管理器
- 2025-04-10 09:45:00
- admin 原创
- 15
问题描述:
我正在尝试理解该with
语句。我理解它应该替换try
/except
块。
现在假设我做这样的事情:
try:
name = "rubicon" / 2 # to raise an exception
except Exception as e:
print("No, not possible.")
finally:
print("OK, I caught you.")
我如何使用上下文管理器来代替它?
解决方案 1:
contextlib.contextmanager函数装饰器提供了一种方便的方式来提供上下文管理器,而无需编写ContextManager
自己的完整类(使用__enter__
和__exit__
方法,因此您不必记住__exit__
方法的参数,或者方法__exit__
必须使用才能抑制异常)。相反,您可以在希望块运行的位置return True
编写一个带有单个的函数,然后像平常一样捕获任何异常(实际上来自)。yield
`with`yield
from contextlib import contextmanager
@contextmanager
def handler():
# Put here what would ordinarily go in the `__enter__` method
# In this case, there's nothing to do
try:
yield # You can return something if you want, that gets picked up in the 'as'
except Exception as e:
print("no not possible")
finally:
print("Ok I caught you")
with handler():
name='rubicon'/2 #to raise an exception
为什么要费力编写上下文管理器呢?代码重用。您可以在多个地方使用相同的上下文管理器,而不必重复异常处理。如果异常处理是该情况所独有的,那么就不必使用上下文管理器。但如果相同的模式一次又一次出现(或者对于您的用户来说可能如此,例如关闭文件、解锁互斥锁),那么值得付出额外的努力。如果异常处理有点复杂,这也是一个很好的模式,因为它将异常处理与代码流的主线分开。
解决方案 2:
with
并没有真正取代try
/ except
,而是try
/ finally
。不过,你可以让上下文管理器在异常情况下和非异常情况下执行不同的操作:
class Mgr(object):
def __enter__(self): pass
def __exit__(self, ext, exv, trb):
if ext is not None: print "no not possible"
print "OK I caught you"
return True
with Mgr():
name='rubicon'/2 #to raise an exception
该return True
部分是上下文管理器决定抑制异常的地方(就像您不在子句中重新引发它一样except
)。
解决方案 3:
Pythonwith
中的 用于包装一组语句,您可以在其中设置、销毁或关闭资源。try...finally
在这方面,它与 finally 子句类似,因为即使在发生异常后也会执行。
上下文管理器是一个实现两种方法的对象:__enter__
和__exit__
。它们分别在with
块之前和之后被调用。
例如,看一下经典的open()
例子:
with open('temp.txt', 'w') as f:
f.write("Hi!")
Open 返回一个实现或多或少类似和类似的File
对象。__enter__
`return self__exit__
self.close()`
解决方案 4:
上下文管理器的组件
你应该实现一个返回对象的__enter__方法
实现exit方法。
例子
我举个简单的例子来说明为什么需要上下文管理器。中国新疆的冬天,开门要立刻关门,如果忘记关门,就会着凉。
class Door:
def __init__(self):
self.doorstatus='the door was closed when you are not at home'
print(self.doorstatus)
def __enter__(self):
print('I have opened the door')
return self
def __exit__(self,*args):
print('pong!the door has closed')
def fetchsomethings(self):
print('I have fetched somethings')
回家拿东西的时候,应该打开门,拿东西,然后关门。
with Door() as dr:
dr.fetchsomethings()
输出为:
the door was closed when you are not at home
I have opened the door
I have fetched somethings
pong!the door has closed
解释
当你启动一个 Door 类时,它将调用init方法,该方法将打印“当你不在家时门已关闭”,并调用enter方法,该方法将打印“我已打开门”并返回一个名为 dr 的门实例。当在 with 块中调用self.fetchsomethings时,该方法将打印“我已获取某些东西”。当块完成时,上下文管理器将调用exit
方法并打印“pong!门已关闭”。当你不使用 with 关键字时,enter和exit将不会被调用!!!!
解决方案 5:
with
语句或上下文管理器可以帮助提供资源(尽管可能有更多的用途)。
假设您打开了一个文件进行写入:
f = open(path, "w")
现在您有一个打开的文件句柄。在处理文件期间,没有其他程序可以对其进行写入。为了让其他程序对其进行写入,您必须关闭文件句柄:
f.close()
但是,在关闭文件之前发生了错误:
f = open(path, "w")
data = 3/0 # Tried dividing by zero. Raised ZeroDivisionError
f.write(data)
f.close()
现在将发生的是函数或整个程序将退出,同时留下一个打开的句柄。(CPython 在终止时清理句柄,句柄与程序一起释放,但您不应该依赖这一点)
with 语句确保只要你离开它的缩进,它就会关闭文件句柄:
with open(path, "w") as f:
data = 3/0 # Tried dividing by zero. Raised ZeroDivisionError
f.write(data)
# In here the file is already closed automatically, no matter what happened.
with
语句可以用于更多用途。例如:threading.Lock()
lock = threading.Lock()
with lock: # Lock is acquired
do stuff...
# Lock is automatically released.
几乎所有使用上下文管理器完成的事情都可以用,try: ... finally: ...
但是上下文管理器使用起来更方便,更舒适,更易读,并且通过实现__enter__
和__exit__
提供易于使用的界面。
创建上下文管理器是通过在普通类中实现__enter__()
和完成的。__exit__()
__enter__()
告诉当上下文管理器启动时以及当上下文管理器存在时该做什么(如果发生异常,__exit__()
则将异常传递给方法)__exit__()
创建上下文管理器的快捷方式可以在contextlib中找到。它将生成器包装为上下文管理器。
解决方案 6:
管理资源:在任何编程语言中,文件操作或数据库连接等资源的使用都非常常见。但这些资源的供应有限。因此,主要问题在于确保在使用后释放这些资源。如果不释放它们,则会导致资源泄漏,并可能导致系统变慢或崩溃。如果用户拥有一种自动设置和拆除资源的机制,那将非常有帮助。在 Python 中,可以通过使用上下文管理器来实现,这有助于正确处理资源。执行文件操作的最常见方式是使用关键字,如下所示:
# Python program showing a use of with keyword
with open("test.txt") as f:
data = f.read()
打开文件时会消耗文件描述符,这是一种有限的资源。一个进程一次只能打开一定数量的文件。以下程序演示了这一点。
file_descriptors = []
for x in range(100000):
file_descriptors.append(open('test.txt', 'w'))
它导致错误:OSError: [Errno 24] Too many open files: 'test.txt'
Python 提供了一种管理资源的简单方法:上下文管理器。使用 with 关键字。当它被评估时,它应该产生一个执行上下文管理的对象。上下文管理器可以使用类或函数(带装饰器)编写。
创建上下文管理器:使用类创建上下文管理器时,用户需要确保该类具有以下方法:__enter__()
和__exit__()
。__enter__()
返回需要管理的资源,__exit__()
不返回任何内容但执行清理操作。首先,让我们创建一个名为 ContextManager 的简单类来了解使用类创建上下文管理器的基本结构,如下所示:
# Python program creating a context manager
class ContextManager():
def __init__(self):
print('init method called')
def __enter__(self):
print('enter method called')
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
print('exit method called')
with ContextManager() as manager:
print('with statement block')
Output:
init method called
enter method called
with statement block
exit method called
在这种情况下,将创建一个 ContextManager 对象。它被分配给关键字 ie manager 后的变量。运行上述程序时,将按顺序执行以下操作:
__init__()
__enter__()
语句主体(with 块内的代码)
__exit__()
【该方法中的参数用于管理异常】
扫码咨询,免费领取项目管理大礼包!