When we execute the statement block, we need some preparation actions, and after the execution is complete, we need to perform some finishing actions. For example: the file needs to be closed after reading and writing, the connection needs to be closed after the database is read and written, and the resources are locked and unlocked. In this case, Python provides the concept of context management, which can be used to process the preparation actions before the execution of the code block and the finishing actions after the execution through the context manager.
First look at the situation where you can't use the context manager
f =open("log.txt","w")try:
f.write("hello")finally:
f.close()
Use context manager
withopen("log.txt","w")as f:
f.write("hello")
When the statement ends, Python will automatically call the f.close()
method for us
The
entermethod of the object returned by the
open("log.txt", "w") statement after with
will be called, and the return value of __enter__
will be assigned to the variable after as
When with
is executed, the __exit__
method of the front-end return object will be called.
Let's write a class to test:
classContextTest:
def __enter__(self):print("Enter enter!")return"Foo"
def __exit__(self,*args,**kwargs):print("Enter exit!")
def get_sample():returnContextTest()withget_sample()as sample:print(f"Sample: {sample}")
operation result:
Enter enter!
Sample: Foo
Enter exit!
According to the principle of the with statement above, we use the class to implement a class that supports the with statement to open files
classFile:
def __init__(self, file_name: str, method: str):
self.file_obj =open(file_name, method)
def __enter__(self):return self.file_obj
def __exit__(self, exc_type, exc_val, exc_tb):
self.file_obj.close()print(f"type: {exc_type}")print(f"value: {exc_val}")print(f"traceback: {exc_tb}")withFile('demo.txt',"w")as f:
f.useless_func()
We also implemented an exception handling here. Regarding exception handling, the with statement will be executed like this
exc_type
, exc_val
, exc_tb
to the __exit__
method__exit__
method to handle exceptions__exit__
returns True, then the exception is ignored.__exit__
returns anything other than True, then this exception will be thrown by the with statement.The output result is:
type:<class'AttributeError'>
value:'_io.TextIOWrapper' object has no attribute 'useless_func'
traceback:<traceback object at 0x000001D259D82908>Traceback(most recent call last):...
AttributeError:'_io.TextIOWrapper' object has no attribute 'useless_func'
from contextlib import contextmanager
@ contextmanager
def my_open(filename, mode):
file_obj =open(filename, mode)try:yield file_obj.readlines()
except Exception as e:
raise e
finally:
file_obj.close()if __name__ =='__main__':withmy_open(r'demo.txt','r')as f:for line in f:print(line)
The code before yield is executed by the __enter__
method, and the code after yield is executed by the __exit__
method. Essentially, it is the __enter__
and __exit__
methods. Because the decorator of @contextmanager
accepts a generator, use the yield
statement to output the variables of with ... as var
, and then the with statement can work normally
In addition to taking over the opening and closing of files, databases, etc., we can also use the features of @contextmanager
to do some great things. If we want to automatically execute specific codes before and after a certain piece of code is executed, we can also use @contextmanager
to achieve
@ contextmanager
def tag(name):print("<%s>"% name)yieldprint("</%s>"% name)withtag("h1"):print("hello")print("world")
# Output
< h1>
hello
world
< /h1>
If an object does not implement a context, we cannot use it in the with
statement. At this time, we can use closing
to turn the object into a context object. For example, use urlopen()
with with
statement
from contextlib import closing
from urllib.request import urlopen
withclosing(urlopen('https://www.python.org'))as page:for line in page:print(line)
Let's look at the source code of this magical closing
classclosing(AbstractContextManager):"""Context to automatically close something at the end of a block.
Code like this:withclosing(<module>.open(<arguments>))as f:<block>
is equivalent to this:
f =<module>.open(<arguments>)try:<block>finally:
f.close()"""
def __init__(self, thing):
self.thing = thing
def __enter__(self):return self.thing
def __exit__(self,*exc_info):
self.thing.close()
It's the same as what we did with the class method above~
Liao Xuefeng's Python tutorial-contextlibTwo implementation methods of python with statement context managementUse and principle of with in Python
Recommended Posts