Dive into Python -
This is a small snippet from fileinfo.py
used in the book. This is opening an MP3 file and reading the last 128 bytes to fetch and later parse the metadata.
try:
fsock = open(filename, "rb", 0)
try:
fsock.seek(-128, 2)
tagdata = fsock.read(128)
finally:
fsock.close()
.
. # process tagdata: will NEVER raise IOError though
.
except IOError:
pass
This can be refactored as:
try:
fsock = open(filename, "rb", 0)
try:
fsock.seek(-128, 2)
tagdata = fsock.read(128)
except IOError:
pass
finally:
fsock.close()
.
. # process tagdata
.
I even used to have this question when I was learning Java
. Should we just keep the logic that can actually raise an exception inside the 开发者_运维问答try..except
block or for the sake of keeping a code that does one particular job in ONE place; keep the other code that will NEVER raise an exception also within a try...except
?
The try
/finally
clause's primary purpose is to close the file regardless of what happens, it doesn't make sense to move it the outer try
/except
as I assume you are trying to do:
try:
fsock = open(filename, "rb", 0)
try:
fsock.seek(-128, 2)
tagdata = fsock.read(128)
except:
pass
except IOError:
pass
finally:
fsock.close()
The reason being, if IOError
is actually raised, calling fsock.close()
would raise another exception, since fsock
would not have been assigned. Instead of either, it'd be preferable to use the with
statement which will automatically close the file for you:
try:
with open(filename, 'rb') as fsock:
fsock.seek(-128, 2)
tagdata = fsock.read(128)
except IOError:
pass
The most accepted standard is to put as little code in the try..except as possible. Reasoning is that you don't know what the other code will raise if there's a ton of code in a try.. then it becomes really messy.
You can see lots of good styling information in PEP 8, amongst which is:
- Additionally, for all try/except clauses, limit the 'try' clause
to the absolute minimum amount of code necessary. Again, this
avoids masking bugs.
Yes:
try:
value = collection[key]
except KeyError:
return key_not_found(key)
else:
return handle_value(value)
No:
try:
# Too broad!
return handle_value(collection[key])
except KeyError:
# Will also catch KeyError raised by handle_value()
return key_not_found(key)
The second piece of code is syntactically invalid, so you should prefer the first form.
If you'd make it syntactically valid by adding an except
or finally
clause, it would be semantically invalid: if the open
fails, you'd still be trying to close fsock
, which would not be assigned.
If the open
fails then you can't assign to tagdata
, so you should not allow the code to reach the point where you process tagdata
. Often the best way to handle this is to process the IOError
at a higher level (i.e. wrap this in a function and handle it in the calling context).
BTW, in modern Python we don't need to use finally
for this sort of thing - we have a more powerful idiom. We also have an else
clause that can be attached to try/except blocks that is executed only if the exception handlers are not invoked.
So we get something like:
def get_data():
with open(filename, "rb", 0) as fsock:
fsock.seek(-128, 2)
return fsock.read(128)
def do_processing():
try: tagdata = get_data()
except IOError: handle_error()
else: process(tagdata)
精彩评论