回炉-Python基础教程-3

  |  

摘要: 《Python基础教程》笔记 part3

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


写在前面

【回炉-Python基础教程】系列连载主要回炉我之前看过的一本比较入门但是很系统的 Python 的书。涉及书中章节 Chap8、Chap11,各章节记录的要点如下:

本书信息:

本书包括Python程序设计的方方面面,主要分为 5 个部分

  • Python基础知识和基本概念,包括列表、元组、字符串、字典以及各种语句
  • Python相对高级的主题,包括抽象、异常、魔法方法、特性、迭代器、文件处理
  • Python与数据库、网络、C语言等工具结合使用,发挥出Python的强大功能
  • Python程序测试、打包、发布等知识
  • 10个具有实际意义的Python项目的开发过程

往期回顾


本文是《Python基础教程》回炉系列的第二部分中的异常、文件处理这个话题中的一些容易忘的点。

  • Chap 8 Exception
    • What Is an Exception?
    • Catching Exceptions
    • Exceptions and Functions
    • The Zen of Exceptions
    • Not All That Exceptional
  • Chap 11 Files and Stuff
    • Opening Files
    • The Basic File Methods
    • Iterating over File Contents

8. Exception

(1) What Is an Exception?

exception objects

When it encounters an error, it raises an exception. If such an exception object is not handled (or caught), the program terminates with a so-called traceback.

raise statement with an argument that is either a class (which should subclass Exception) or an instance.

Some Built-in Exceptions

类名 描述
Exception The Base class for almost all exceptions
AttributeError Raised when attribute reference or assignment fails
OSError Raised when the operating system can’t perform a task, such as a file, for example. Has several specific subclasses
IndexError Raised when using a nonexistent index on a sequence. Subclass of LookupError
KeyError Raised when using a nonexistent key on a mapping. Subclass of LookupError
NameError Raised when a name(variable) is not found
SyntaxError Raised when the code is ill-formed
TypeError Raised when a built-in operation or function is applied to an object of the wrong type
ValueError Raised when a built-in operation or function is applied to an object with the correct type but with an inappropriate value
ZeroDivisionError Raised when the second argument of a division or module operation is zero

(2) Catching Exceptions

try/except statement

1
2
3
4
5
6
try:
x = int(input('Enter the first number: '))
y = int(input('Enter the second number: '))
print(x / y)
except ZeroDivisionError:
print("The second number can't be zero!")

Exceptions propagate out of functions to where they’re called, and if they’re not caught there either, the exceptions will “bubble up” to the top level of the program. This means that you can use try / except to catch exceptions that are raised in other people’s functions. For more details, see the section “Exceptions and Functions” later in this chapter.

If you have caught an exception but you want to raise it again (pass it on, so to speak), you can call raise without any arguments

1
2
3
4
5
6
7
8
9
10
class MuffledCalculator:
muffled = False
def calc(self, expr):
try:
return eval(expr)
except ZeroDivisionError:
if self.muffled:
print("Division by zero is illegal")
else:
raise

If division by zero occurs and muffling is turned on, the calc method will (implicitly) return None. In other words, if you turn on muffling, you should not rely on the return value.

Using raise with no arguments is often a good choice in an except clause, if you’re unable to handle the exception.

Sometimes you may want to raise a different exception, though. In that case, the exception that took you into the except cause will be stored as the context for your exception and will be part of the final error message, for example:

1
2
3
4
try:
1/0
except ZeroDivisionError:
raise ValueError
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-2-e039887c3d95> in <module>
1 try:
----> 2 1/0
3 except ZeroDivisionError:

ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

ValueError Traceback (most recent call last)
<ipython-input-2-e039887c3d95> in <module>
2 1/0
3 except ZeroDivisionError:
----> 4 raise ValueError
5

You can supply your own context exception by using the raise … from … version of the statement or use None to suppress the context.

1
2
3
4
try:
1/0
except ZeroDivisionError:
raise ValueError from None
1
2
3
4
5
6
7
8
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-3-d9388897f61c> in <module>
2 1/0
3 except ZeroDivisionError:
----> 4 raise ValueError from None

ValueError:

More Than One except Clause

1
2
3
4
5
6
7
8
try:
x = int(input("Enter the first number: "))
y = int(input("Enter the second number: "))
print(x / y)
except ZeroDivisionError:
print("The second number can't be zero!")
except TypeError:
print("That wasn't a number, was it?")

This time using an if statement would be more difficult. How do you check whether a value can be used in division? There are a number of ways, but by far the best way is, in fact, to simply divide the values to see if it works.

Catching Two Exceptions with One Block

1
2
3
4
5
6
try:
x = int(input("Enter the first number: "))
y = int(input("Enter the second number: "))
print(x / y)
except (ZeroDivisionError, TypeError, NameError):
print("Your numbers were bogus ...")

Catching the Object

If you want access to the exception object itself in an except clause, you can use two arguments instead of one

This can be useful (for example) if you want your program to keep running but you want to log the error somehow (perhaps just printing it out to the user). The following is a sample program that prints out the exception (if it occurs) but keeps running:

1
2
3
4
5
6
try:
x = int(input("Enter the first number: "))
y = int(input("Enter the second number: "))
print(x / y)
except (ZeroDivisionError, TypeError) as e:
print(e)

A Real Catchall

Even if the program handles several types of exceptions, some may still slip through. For example, using the same division program, simply try to press Enter at the prompt, without writing anything. You should get an error message and some information about what went wrong (a stack trace),

1
ValueError: invalid literal for int() with base 10: ''

This exception got through the try/except statement—and rightly so. You hadn’t foreseen that this could happen and weren’t prepared for it. In these cases, it is better that the program crash immediately (so you can see what’s wrong) than that it simply hide the exception with a try/except statement that isn’t meant to catch it.

However, if you do want to catch all exceptions in a piece of code, you can simply omit the exception class from the except clause

1
2
3
4
5
6
try:
x = int(input("Enter the first number: "))
y = int(input("Enter the second number: "))
print(x / y)
except:
print("Something wrong happened...")

Catching all exceptions like this is risky business because it will hide errors you haven’t thought of as well as those you’re prepared for.

It will also trap attempts by the user to terminate execution by Ctrl-C, attempts by functions you call to terminate by sys.exit, and so on.
In most cases, it would be better to use except Exception as e and perhaps do some checking on the exception object, e. This will then permit those very few exceptions that don’t subclass Exception to slip through. This includes SystemExit and
KeyboardInterrupt, which subclass BaseException, the superclass of Exception itself.

When All Is Well

In some cases, it can be useful to have a block of code that is executed unless something bad happens; as with conditionals and loops, you can add an else clause to the try/except statement.

1
2
3
4
5
6
try:
print("A simple task")
except:
print("What? Something went wrong?")
else:
print("Ah... It went as planned.")

Here, the loop is broken (by the break statement in the else clause) only when no exception is raised.

1
2
3
4
5
6
7
8
9
10
while True:
try:
x = int(input("Enter the first number: "))
y = int(input("Enter the second number: "))
value = x / y
print("x / y is", value)
except:
print("Invalid input. Please try again.")
else:
break

Finally

You use it to do housekeeping after a possible exception.

combine try, except, finally, and else,in a single statement.

1
2
3
4
5
6
7
8
try:
1 / 0
except NameError:
print("Unknown variable")
else:
print("That went well!")
finally:
print("Cleaning up.")

(3) Exceptions and Functions

If an exception is raised inside a function and isn’t handled there, it propagates (bubbles up) to the place where the function was called. If it isn’t handled there either, it continues propagating until it reaches the main program (the global scope), and if there is no exception handler there, the program halts with a stack trace.

(4) The Zen of Exceptions

1) 有时可以条件判断

1
2
3
4
5
def describe_person(person):
print("Description of", person["name"])
print("Age:", person["age"])
if "occupation" in person:
print("Occupation: ", person["occupation"])

It has to look up the key ‘occupation’ twice—once to see whether the key exists (in the condition) and once to get the value (to print it out). An alternative definition is as follows:

1
2
3
4
5
6
7
def describe_person(person):
print("Description of", person["name"])
print("Age:", person["age"])
try:
print("Occupation: ", person["occupation"])
except KeyError:
pass

2) object has a specific attribute

In general (unless your program is having performance problems), you shouldn’t worry about that sort of optimization too much. The point is that using try/except statements is in many cases much more natural (more “Pythonic”) than if/else, and you should get into the habit of using them where you can

(5) Not All That Exceptional

If you just want to provide a warning that things aren’t exactly as they should be, you could use the warn function from the warnings module.

Other code using your module can suppress your warnings, or only specific kinds of warnings, using the filterwarnings function from the same module, specifying one of several possible actions to take, including “error” and “ignore”.

1
2
3
4
5
from warnings import filterwarnings
filterwarnings("ignore")
warn("Anyone out there?")
filterwarnings("error")
warn("Something is very wrong!")
1
2
3
4
5
6
7
8
---------------------------------------------------------------------------
UserWarning Traceback (most recent call last)
<ipython-input-28-540da868e45e> in <module>
3 warn("Anyone out there?")
4 filterwarnings("error")
----> 5 warn("Something is very wrong!")

UserWarning: Something is very wrong!

11. Files and Stuff

Opening Files

open() 函数在io 模块里,io 是自动 import 的模块,因此 open() 可以直接使用。

file mode

  • ‘r’: default, 文件不存在则FileNotFoundError
  • ‘w’: write mode, 文件存在则覆盖
  • ‘x’: executable write mode, 文件存在则FileExistsError
  • ‘a’: append mode
  • ‘b’: Binary mode(added to other mode)
  • ‘t’: Text mode(default, add to other mode)
  • ‘+’: Read/Write mode(added to other mode), ‘w+’会删除原有内容,’r+’不会删除原有内容

默认情况下为’rt’, treated as encoded Unicode File, 自动编解码, 默认UTF-8

The Basic File Methods

Basic method of file object and file-like object, sometimes called streams

A file-like object is simply one supporting a few of the same methods as a file, most notably either read or write or both. 例如 urlopen() 返回的对象

Reading and Writing

1
2
f.write(string)
f.read(4) # Tell how many character or bytes you want to read

Piping output

例子

1
2
3
4
5
6
# somescript.py
import sys
text = sys.stdin.read()
words = text.split()
wordcount = len(words)
print('Wordcount:', wordcount)
1
cat somefile.txt | python somescript.py | sort

random Access — seek and tell(两种 file-like object)

1
seek(offset[, whence])

moves the current position (where reading or writing is performed) to the position described by offset and whence.

offset is a byte (character) count. whence defaults to io.SEEK_SET or 0 , which means that the offset is from the beginning of the file.

whence may also be set to io.SEEK_CUR or 1(move relative to current position; the offset may be negative). or io.SEEK_END or 2 (move relative to the end of the file).

Reading and Writing lines

The method writelines is the opposite of readlines:

give it a list (or, in fact, any sequence or iterable object) of strings, and it writes all the strings to the file (or stream). Note that newlines are not added; you need to add those yourself.

losing Files

You should always close a file you have written to because Python may buffer (keep stored temporarily somewhere, for efficiency reasons) the data you have written, and if your program crashes for some reason, the data might not be written to the file at all.

If you want to reset the buffering and make your changes visible in the actual file on disk but you don’t yet want to close the file, you can use the flush method.

flush might not allow other programs running at the same time to access the file because of locking considerations that depend on your operating system and settings.

1
2
3
4
5
# Open your file here
try:
# Write data to your file
finally:
file.close()
1
2
with open("somefile.txt") as somefile:
do_something(somefile)

关于上下文管理器(context manager)

A context manager is an object that supports two methods: __enter__ and __exit__

The __enter__ method takes no arguments. It is called when entering the with statement, and the return value is bound to the variable after the as keyword.

The __exit__ method takes three arguments: an exception type, an exception object, and an exception traceback. It is called when leaving the method (with any exception raised supplied through the parameters). If exit returns false, any exceptions are suppressed.

Iterating over File Contents

1. One Character (or Byte) at a Time

1
2
3
4
5
with open(filename) as f:
while True:
char = f.read(1)
if not char: break
process(char)

2. One Line at a Time

1
2
3
4
5
with open(filename) as f:
while True:
line = f.readline()
if not line: break
process(line)

3. Reading Everything (前提文件较小)

Iterating over character

1
2
3
with open(filename) as f:
for char in f.read():
process(char)

Iterating over lines

1
2
3
with open(filename) as f:
for line in f.readlines():
process(line)

4. Lazy Line Iteration with fileinput

You can use a method called lazy line iteration—it’s lazy because it reads only the parts of the file actually needed

Iterating over Lines with fileinput

1
2
3
import fileinput
for line in fileinput.input(filename):
process(line)

5. File Iterators

Files are actually iterable, which means that you can use them directly in for loops to iterate over their lines.

1
2
3
with open(filename) as f:
for line in f:
process(line)

Iterating over a File Without Storing the File Object in a Variable

1
2
for line in open(filename):
process(line)

sys.stdin is iterable, just like other files

1
2
3
import sys
for line in sys.stdin:
process(line)

Share