with and contextmanager

with

初识with

在Python中,读写文件这样的资源要特别注意,必须在使用完毕后正确关闭它们。正确关闭文件资源的一个方法是使用try...finally

try:
    f = open("/directory/filename", 'r')
    f.read()
finally:
      # 如果f不存在, 则应该是文件对象未打开
    if f:
        f.close()

但是写try:...finally:...非常繁琐。Python的with语句允许我们非常方便地使用资源,而不必担心资源没有关闭,所以上面的代码可以简化为:

# 注意f只会在with语句块有效
with open("/directory/filename", 'r') as f:
        f.read()

with的工作原理

并不是只有open()函数返回的fp对象才能使用with语句。实际上,任何对象,只要正确实现了上下文管理,就可以用于with语句。

实现上下文管理是通过__enter____exit__这两个方法实现的. 我们可以简单通过一个例子来说明with的内部调用方法

class Generator(object):

    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print("There's no way out")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
          # 检查退出的类型
        # 当有异常发生时 exc_type存在值
        if exc_type:
            print('Open the door')
        else:
            print('100%')

    def generate(self):
        print('%s can uses love to generate electricity' % self.name)

我们可以通过以下代码调用

# 紧跟with后面的语句被求值后,返回对象的 `__enter__() `方法被调用,
# 这个方法的返回值将被赋值给as后面的变量    
with Generator("LEX") as lex:
      lex.generate()
# 当with后面的代码块全部被执行完之后,将调用返回对象的 `__exit__()`方法。
用爱发电

其实 with 就是这么简单, 大部分初学者只要理解到这里就可以了, 所以小白勿入, 高能级别

高能预警

上下文管理器

我知道有很多小白还是读了, 那我先介绍几个名词让你们知男而退

  • 上下文管理协议(Context Management Protocol):包含方法 __enter____exit__,支持

该协议的对象要实现这两个方法。

  • 上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了

__enter____exit__方法。上下文管理器定义执行 with 语句时要建立的运行时上下文,

负责执行 with 语句块上下文中的进入与退出操作。通常使用 with 语句调用上下文管理器,

也可以通过直接调用其方法来使用。

  • 运行时上下文(runtime context):由上下文管理器创建,通过上下文管理器的 __enter__

__exit__ 方法实现,__enter__ 方法在语句体执行之前进入运行时上下文,__exit__

语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。

  • 上下文表达式(Context Expression):with 语句中跟在关键字 with 之后的表达式,该表达式

要返回一个上下文管理器对象。如 例子with后面的Generator("LEX") as lex:

  • 语句体(with-body):with 语句包裹起来的代码块,在执行语句体之前会调用上下文管

理器的__enter__方法,执行完语句体之后会执行 __exit__ 方法。

其实这些术语不需要你们立刻理解, 只需要在以后的课程或者项目中慢慢体会.

@contextmanager

编写__enter____exit__仍然很繁琐,因此Python的标准库contextlib提供了更简单的写法, 让我们更加简洁的定制自己的上下文管理器. 同样的爱之代码走起:

from context import contextmanager

class Generator(object):

    def __init__(self, name):
        self.name = name

    def generate(self):
        print('%s can uses love to generate electricity' % self.name)

@contextamnager
def create_generator(name):
      print("I'm generating electricity from love again")
      lex = Generator(name)
    # 返回给with...as...语句
    yield lex
    print('100%')

@contextmanager这个decorator必须接受一个 ==generator== ,用yield语句把with ... as var把变量输出出去, 然后属于你的上下文管理器就弄了, 是不是出奇的简单

好久没有这么简单

使用一下这个管理器, 说说感觉

with create_generator("LEX") as lex:
      lex.generate()
感觉良好

@closing

但是少年郎们还没结束呢, 让我们了解一下closing 的作用, 它的作用就是让不是上下文管理器的对象转化为上下文管理器, 即可使用with语句

urllib.request import urlopen

with closing(urlopen('https://www.bilibili.com')) as page:
    for line in page:
        print(line)

其实closing内部非常好实现, 内部还是调用了contextmanager方法

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()

closing 不同于 @contextmanager, 他的本身不需要生成器, 可谓加上这行代码, 秒变真男人

总结

  1. with 关联着对象的__exit____enter__方法, 你必须清楚他们是何时被调用, 他们的返回值又返回到哪里去了

  2. @contextmanager 所装饰的对象必须是带yield的生成器, yield 会把后面的值返回给with...as...语句

  3. @closing 原先必许在所装饰的对象定义一个close方法, 否则会报错, 但是现在不会, 所以这种一行真男人得灵药不多了

  4. 上下文管理器将会贯穿大型框架结构, 如flask的 Application上下文, 请求(request)上下文等等, 学习上下文管理器可以帮助我们更好理解那些框架的工作原理.

未完待续...

引用

廖雪峰 contextlib

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 转载自:http://mp.weixin.qq.com/s/LO1yyFeUA6pR_YPyfDoSig 姓名:梅...
    虐先森阅读 1,418评论 0 1
  • 引言 with 语句是从 Python 2.5 开始引入的一种与异常处理相关的功能(2.5 版本中要通过 from...
    氨基钠阅读 340评论 0 2
  • 本文转自浅谈Python的with语句 引言 with 语句是从 Python 2.5 开始引入的一种与异常处理相...
    Syfun阅读 4,003评论 0 50
  • contextlib — Context Manager Utilities contextlib - 上下文管理...
    英武阅读 2,701评论 0 52
  • 术语 要使用 with 语句,首先要明白上下文管理器这一概念。有了上下文管理器,with 语句才能工作。下面是一组...
    lmem阅读 294评论 0 0