August Rush

一个还在努力成长的小火汁!

游龙当归海,海不迎我自来也。

We create our own demons.

You can reach me at augustrush0923@gmail.com
python上下文管理器和else块
发布:2020年12月15日 | 作者:augustrush | 阅读量: 372

if语句之外的else块


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块代码。


上下文管理器和with块


对于系统资源如文件、数据库连接、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语句防范错误。


上一篇:property属性 下一篇:2020年终总结


  • 标签云

  • 支付宝扫码支持一下

  • 微信扫码支持一下



基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建

版权所有 © 2020-2021 August Rush

京ICP备20007446号-1 & 豫公网安备 41100202000460号

网站地图 & RSS | Feed