Let's start with a common problem, file open:
1234567 |
try : f = open ( ' xxx ' Code class= "python spaces" > do something except : do something finally Code class= "Python plain" >: f.close () |
In fact, I personally see this on the Internet more than once, this is wrong.
The first is correct as follows:
1234567891011 |
try
:
f
= open
(
‘xxx‘
)
except
:
print ‘fail to open‘
exit(
-
1
)
try
:
do something
except
:
do something
finally
:
f.close()
|
It's a lot of trouble, but the right way to do that is to write.
Why do we write finally because it prevents the program from throwing an exception and finally cannot close the file, but the need to close the file is a prerequisite that the file is already open.
In the first error code, if the exception occurs at F=open (' xxx '), such as the file does not exist, it is immediately possible to know that execution of f.close () is meaningless. The corrected solution is the second piece of code.
Okay, I'm going to start talking about the with syntax.
First, we start with the following question, Try-finally's grammatical structure:
12345 |
set things up try : do something finally : tear things down |
This thing is a common structure, such as file Open, it means set things up
f=open(‘xxx‘)
tear things down
f.close()
. In such things as multi-threaded locks, resource requests, eventually there is a need to release. The try...finally structure ensures that the tear things down will always be executed, even if the above do something work is not fully executed.
If this structure is often used, we can first take a more elegant approach to encapsulation!
1234567891011 |
def controlled_execution(callback):
set things up
try
:
callback(thing)
finally
:
tear things down
def my_function(thing):
do something
controlled_execution(my_function)
|
Encapsulation is a good way to support code reuse, but this approach is very dirty, especially if you have to modify some local variables in the Do something (it becomes a function call, which brings trouble to the scope of the variable).
Another option is to use the generator, but only to generate the data once, and we use the for-in structure to call him:
123456789 |
def controlled_execution():
set things up
try
:
yield thing
finally
:
tear things down
for thing
in controlled_execution():
do something with thing
|
Because thing has only one, the yield statement needs to be executed only once. Of course, this is simply awful from the point of view of code readability, which is elegant. We still use the for loop when we're sure that the for loop executes only once, and it's hard to understand what the loop here is for someone who doesn't know.
The ultimate solution for the Python-dev team. (Python 2.5 adds syntax for the with expression later)
123456789 |
class controlled_execution:
def __enter__(
self
):
set things up
return thing
def __exit__(
self
,
type
, value, traceback):
tear things down
with controlled_execution() as thing:
do something
|
Here, Python uses the syntax of With-as. When Python executes this sentence, it invokes the __ENTER__ function and passes the value of the function return to the variable specified after as. After that, Python executes the block of statements below do something. Finally, no matter what exception occurs in the statement block, the __exit__ is executed when it leaves.
In addition, __exit__ in addition to tear things down, can also be abnormal monitoring and processing, note the following several parameters. To skip an exception, you only need to return the function true. The following sample code skips all TypeError and throws the other exceptions normally.
12 |
def __exit__( self , type , value, traceback): return isinstance (value, TypeError) |
After python2.5 and later, the file object has written the __enter__ and __exit__ functions, which we can test:
123456789101112 |
>>> f
= open
(
"x.txt"
)
>>> f
<
open file ‘x.txt‘
, mode
‘r‘ at
0x00AE82F0
>
>>> f.__enter__()
<
open file ‘x.txt‘
, mode
‘r‘ at
0x00AE82F0
>
>>> f.read(
1
)
‘X‘
>>> f.__exit__(
None
,
None
,
None
)
>>> f.read(
1
)
Traceback (most recent call last):
File "<stdin>"
, line
1
,
in <module>
ValueError: I
/
O operation on closed
file
|
After that, we only need to do this if we want to open the file and make sure to close it last:
123 |
with open ( "x.txt" ) as f: data = f.read() do something with data |
If there are multiple items, we can write this:
12 |
with open ( "x.txt" ) as f1, open ( ‘xxx.txt‘ ) as f2: do something with f1,f2 |
As mentioned above, the __EXIT__ function can handle partial exceptions, and if we do not handle exceptions in this function, he will throw them normally, which is what we can write (Python version 2.7 and above, the previous version reference uses the contextlib.nested library function):
12345 |
try : with open ( "a.txt" ) as f : do something except xxxError: do something about exception |
In summary, the WITH-AS expression greatly simplifies the work of the Finally, which is very helpful for maintaining the elegance of the code.
Go With...as in Python ...