if/else中和else是同级对立的语句,对立是指流程经过一层if/else语句只能对应一种处理语句。而else在for/else,while/else,try/else语句中的功能则截然不同。
for/else: for循环没有被break语句中止才运行else块代码。
while/else: while循环没有被break语句中止才运行else块代码。
try/else: try块中,没有异常抛出时才运行else块代码。
对于系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源。
比如 Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,否则会出现什么情况呢?极端情况下会出现 "Too many open files" 的错误,因为系统允许你打开的最大文件数量是有限的。
同样,对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 "Can not connect to MySQL server Too many connections",因为数据库连接是一种非常昂贵的资源,不可能无限制的被创建。
正常的文件操作应该是:获取文件句柄---->进行文件读写操作---->关闭文件
def file_operating():
f = open('file.txt', 'w')
f.write('this is a sentence.')
f.close()
这样写有一个潜在的问题,如果在调用 write 的过程中,出现了异常进而导致后续代码无法继续执行,close 方法无法被正常调用,因此资源就会一直被该程序占用者释放。
def file_operate():
f = open('somefile.txt', 'w')
try:
f.write('this is a sentence.')
excpet Exception as e:
print(e)
finally:
f.close()
改良版本的程序是对可能发生异常的代码处进行 try 捕获,使用 try/finally 语句,该语句表示如果在 try 代码块中程序出现了异常,后续代码就不再执行,而直接跳转到 except 代码块。而无论如何,finally 块的代码最终都会被执行。因此,只要把 close 放在 finally 代码中,文件就一定会关闭。
一种更加简洁、优雅的方式就是用 with 关键字。
def file_operate():
with open('somefile.txt', 'w') as f:
f.write('this is a sentence.')
open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。
任何实现了__enter__()
和__exit__()
方法的对象都可称为上下文管理器,上下文管理器对象可以使用 with 关键字。
with块的功能在于简化try/finally模式。with语句在开始运行时会在上下文管理器对象上调用__enter__()方法,而当with语句结束时会调用__exit__()方法。
with语句中的as是可选的,as将__enter__()方法返回的值绑定到as后的变量。
class File:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
# __enter__() 方法返回资源对象,这里就是你将要打开的那个文件对象
print('__enter__调用')
self.f = open(self.filename, self.mode)
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
# __exit__() 方法处理一些清除工作
print('__exit__调用')
self.f.close()
with File('somefile.txt', 'w') as f:
f.wirte('This is a sentence.')
例:MySQL上下文管理器
class SqlConnect:
def __init__(self, database, user, password, host='localhost', port=3306, charset='utf8'):
self.host = host
self.port = port
self.database = database
self.user = user
self.password = password
self.charset = charset
def __enter__(self):
self.conn = connect(host=self.host, port=self.port, database=self.database, user=self.user, password=self.password, charset=self.charset)
self.cur = self.conn.cursor()
return self.cur
def __exit__(self, exc_type, exc_val, exc_tb):
self.cur.close()
self.conn.close()
# 调用
with SqlConnect('database', 'root', '123456') as cur:
cur.execute("select * from info")
items = cur.fetchall()
for item in items:
print(item)
当上下文管理器遇到异常时由__exit__方法处理。传给__exit__方法的三个参数
exc_type: 异常类。
exc_val:异常实例。有时会有参数传给异常结构方法,例如错误消息,这些参数可以使用exc_val.args获取
exc_tb:traceback对象。
如果一切正常,调用exit方法时传入的参数是None,None,None。
python还提供了一个contextmanger
的装饰器,更进一步简化了上下文管理器的实现方式。
通过yield将函数分割成两部分,yield之前的语句在__enter__
方法中执行,yield之后的语句在__exit__
方法中执行。紧跟在yield后面的值是函数的返回值。
from contextlib import contextmanager
@contextmanager
def my_open(path, mode):
f = open(path, mode)
# yield之前的语句在__enter__方法中执行
yield f # yield后面的值是函数的返回值,绑定到实际调用with中的as字句的目标变量上
# yield之后的语句在__exit__方法执行
f.close()
# 调用
with my_open('somefile.txt', 'w') as f:
f.write('This is a sentence.')
例:MySQL上下文管理器
@contextmanager
def sql_connect(database, user, password, host='localhost', port=3306, charset='utf8'):
conn = connect(host=host, port=port, database=database, user=user,
password=password, charset=charset)
cur = conn.cursor()
yield cur
cur.close()
conn.close()
# 调用
with sql_connect('database', 'root', '123456') as cur:
cur.execute("select * from info")
items = cur.fetchall()
for item in items:
print(item)
一旦with块代码在调用方法时出现异常时,抛出的异常会在方法函数中的yield表达式中再次抛出。如果方法函数没有处理异常的代码就会导致函数运行中止,系统处于无效状态。因此必要时在上下文管理器函数中使用try/finally语句防范错误。
基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建