前言

回到顶部

有个学生在第四轮面试中被CTO问到:如何自定义实现with open的功能。然后就一脸懵逼的回来找我了……
看到with open大家都应该知道,这是在问关于上下文管理协议,这个所谓的上下文管理协议一般是指我们在文件操作中常用的那个with ... as ..

Python允许with语句使用上下文管理器,而上下文管理器则是支持__enter____exit__方法的对象。__enter__方法没有参数,当程序执行到with语句时被调用,返回值绑定在文件对象f上,而__exit__方法则有三个参数,包括异常类型,异常对象,异常回溯。

with as是如何工作的

回到顶部

我们通过示例来了解with ... as ...语句是如何工作的,先来个简单的:

class MyOpen1(object):

    def __enter__(self):
print('当with语句开始执行时,我被执行啦…………') def __exit__(self, exc_type, exc_val, exc_tb):
print('当with语句执行完毕,我被执行啦…………')
with MyOpen1() as f1:
print('假装再做文件操作…………') '''打印结果
当with语句开始执行时,我被执行啦…………
假装再做文件操作…………
当with语句执行完毕,我被执行啦…………
'''

上例的打印结果印证了我们之前所说。并且,我们知道当MyOpen1加括号实例化的时候,还触发了实例化方法__init__的执行:

class MyOpen2(object):

    def __init__(self):
print('我是__init__方法,当 {} 加括号时,我被执行啦…………'.format(self.__class__.__name__)) def __enter__(self):
print('当with语句开始执行时,我被执行啦…………') def __exit__(self, exc_type, exc_val, exc_tb):
print('当with语句执行完毕,我被执行啦…………') with MyOpen2() as f2:
print('假装再做文件操作…………') '''打印结果
我是__init__方法,当 MyOpen2 加括号时,我被执行啦…………
当with语句开始执行时,我被执行啦…………
假装再做文件操作…………
当with语句执行完毕,我被执行啦…………
'''

自定制open方法

回到顶部

由上面的两个示例我们隐约可以明白with ... as ..语句内部是怎么玩的了。我们可以试着更深一步的完善代码:

class MyOpen3(object):

    def __init__(self, file_obj, mode='r', encoding='utf-8'):
self.file_obj = file_obj
self.mode = mode
self.encoding = encoding def __enter__(self):
self.f = open(file=self.file_obj, mode=self.mode, encoding=self.encoding)
return self.f def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close() with MyOpen3('a.txt', mode='w', encoding='utf-8') as f3:
f3.write('custom with ... as ...')

结果已经不用多说,完美!

更多的示例

回到顶部

更多的示例和其他的扩展:

class MyOpen(object):
""" 手动实现with open """ def __init__(self, file_obj, mode='r', encoding=None):
"""
当实例化的时候,需要传递三个参数
:param file_obj: 文件名称(路径)
:param mode: 操作模式
:param encoding: 编码
"""
# 调用内置函数open,获取文件对象f
self.f = open(file_obj, mode=mode, encoding=encoding) def __enter__(self):
"""
当with语句被调用时,返回文件对象f
:return: f
"""
print('with语句触发__enter__执行')
return self def __exit__(self, exc_type, exc_value, traceback):
"""
退出关联到此对象的运行时上下文。 各个参数描述了导致上下文退出的异常。 如果上下文是无异常地退出的,三个参数都将为 None
:param exc_type:
:param exc_value:
:param traceback:
:return:
"""
self.f.close()
print('文件对象 f 已被关闭') def read(self):
"""
自定义读方法
"""
print('自定义的read方法被执行')
return self.f.read() def write(self, content):
self.f.write(content) def __getattr__(self, item):
"""
当自定义的文件对象调用MyOpen类中没实现个方法时,就通过getattr,从内置函数的open对象中返回
:param item: 比如seek方法
:return: 将seek方法返回
"""
return getattr(self.f, item) f = MyOpen('a.txt', 'a', "utf8")
f.write('我爱北京天安门')
f.close()
f = MyOpen('a.txt', 'r', "utf8")
print(f.read())
f.close() with MyOpen('a.txt', 'r+', "utf8") as f1:
# print(f1.__doc__) # 通过打印结果可以看到,with语句使用__enter__方法将MyOpen实例化的对象赋值给f1
f1.seek(3) # 注意,由于utf8编码对普通中文用3个字节表示,所以seek的时候,需要注意
f1.write('我是你爸爸')
print(f1.read())

再来一个搭配pickle的:
ps:原谅洒家无耻的拷贝!

import  pickle
class MyPickledump:
def __init__(self,path):
self.path = path def __enter__(self):
self.f = open(self.path, mode='ab')
return self def dump(self,content):
pickle.dump(content,self.f) def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close() class Mypickleload:
def __init__(self,path):
self.path = path def __enter__(self):
self.f = open(self.path, mode='rb')
return self def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close() def load(self):
return pickle.load(self.f) def loaditer(self):
while True:
try:
yield self.load()
except EOFError:
break # with MyPickledump('file') as f:
# f.dump({1,2,3,4}) with Mypickleload('file') as f:
for item in f.loaditer():
print(item) with和pickle

更多的:

import  pickle
class MyPickledump:
def __init__(self,path):
self.path = path def __enter__(self):
self.f = open(self.path, mode='ab')
return self def dump(self,content):
pickle.dump(content,self.f) def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close() class Mypickleload:
def __init__(self,path):
self.path = path def __enter__(self):
self.f = open(self.path, mode='rb')
return self def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close() def __iter__(self):
while True:
try:
yield pickle.load(self.f)
except EOFError:
break # with MyPickledump('file') as f:
# f.dump({1,2,3,4}) with Mypickleload('file') as f:
for item in f:
print(item) with和pickle和iter

see also: Python官网关于with的描述 | PEP 343 with语句 | with 语句上下文管理器 | Context Manager Types | Python概念-上下文管理协议中的__enter__和__exit__ | python之路——面向对象进阶
欢迎斧正,that's all

上下文管理器之__enter__和__exit__的更多相关文章

  1. Python上下文管理协议:__enter__和__exit__

    上下文管理器(context manager)是Python2.5开始支持的一种语法,用于规定某个对象的使用范围.一旦进入或者离开该使用范围,会有特殊操作被调用 (比如为对象分配或者释放内存).它的语 ...

  2. python基础----实现上下文管理协议__enter__和__exit__

    我们知道在操作文件对象的时候可以这么写 with open('a.txt') as f: '代码块' 上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明_ ...

  3. __enter__,__exit__上下文管理协议

    上下文管理协议__enter__,__exit__ 用途或者说好处: 1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预 2.在需要管理一些资源比 ...

  4. (转)Python中的上下文管理器和Tornado对其的巧妙应用

    原文:https://www.binss.me/blog/the-context-manager-of-python-and-the-applications-in-tornado/ 上下文是什么? ...

  5. (转)contextlib — 上下文管理器工具

    原文:https://pythoncaff.com/docs/pymotw/contextlib-context-manager-tool/95 这是一篇社区协同翻译的文章,你可以点击右边区块信息里的 ...

  6. Pthon魔术方法(Magic Methods)-上下文管理

    Pthon魔术方法(Magic Methods)-上下文管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.上下文管理方法 __enter__: 进入与此对象相关的上下文.如果 ...

  7. Python高级笔记(八)with、上下文管理器

    1. 上下文管理器 __enter__()方法返回资源对象,__exit__()方法处理一些清除资源 如:系统资源:文件.数据库链接.Socket等这些资源执行完业务逻辑之后,必须要关闭资源 #!/u ...

  8. Python - Context Manager 上下文管理器

    什么是上下文管理器 官方解释... 上下文管理器是一个对象 它定义了在执行 with 语句时要建立的运行时上下文 上下文管理器处理进入和退出所需的运行时上下文以执行代码块 上下文管理器通常使用 wit ...

  9. Python进阶(上下文管理器与with语句)

    /*上下文管理器必须有__enter__和__exit__方法*/ class MyResource: def __enter__(self): print('链接资源') return self / ...

随机推荐

  1. Python 如何理解可更改元组中的可变序列

    在 Python 中,元组是不可变序列,那为什么当元组中的元素是可变序列时(如 list.dict)可进行增删的操作? 在定义一个元组时,Python内部会为元组中的每一个元素分配一个内存地址,当我们 ...

  2. mybatisplus构造器 condition

    不为null和不为“”的才会加入到sql语句中

  3. 为什么深度(Ubuntu)Linux挂载NTFS分区只读不可写?

      如前所述,经扩展,Dell Vostro 1520笔记本电脑拥有了两块硬盘. 本着旧物利用的心思,在其中一块256GiB固态盘上安装了深度linux,同时挂载另一块2TiB机械盘作为存储盘. 这块 ...

  4. NetworkX系列教程(2)-graph生成器

    小书匠Graph图论 本节主要讲解如何快速使用内置的方法生成graph,官方的文档在这里,里面包含了networkX的所有graph生成器,下面的内容只是我节选的内容,并将graph画出来而已. 声明 ...

  5. List集合类

    1.1:  List.add方法——向集合列表中添加对象 public static void main(String[] args) { List<String> list=new Ar ...

  6. 幽默的讲解六种Socket I/O模型

    很幽默的讲解六种Socket I/O模型 本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教. 一:select模型 二:WSAAsyncSele ...

  7. ID生成算法(一)——雪花算法

    JavaScript生成有序GUID或者UUID,这时就想到了雪花算法. 原理介绍: snowFlake算法最终生成ID的结果为一个64bit大小的整数,结构如下图: 解释: 1bit.二进制中最高位 ...

  8. 在本机上用IO流实现复制粘贴功能

    /** * 复制文件夹 * @param sourcePath * @param targetPath * @throws IOException */ public void copyFolder( ...

  9. 使用Redis sorted set实现集合设置member过期

    在我们日常工作中,有许多这种逻辑 例如需要得到最近三分钟的cache list. 例如我们监控系统需要查询最近一分钟的数据. 总结说来就是 需要一个list存储对象,并且这个对象会无限制增长,需要设置 ...

  10. 【Java】LinkedHashMap

    Java LinkedHashMap 标签(空格分隔):Java source-code 总结 1.LinkedHashMap基于HashMap,实现了按插入顺序.LRU,实现方式主要是继承了Hash ...