- Preface 前言
- 第1章 引论
- 第2章 编程惯用法
- 第3章 基础语法
- 建议19:有节制地使用 from…import 语句
- 建议20:优先使用 absolute import 来导入模块
- 建议21:i+=1 不等于 ++i
- 建议22:使用 with 自动关闭资源
- 建议23:使用 else 子句简化循环(异常处理)
- 建议24:遵循异常处理的几点基本原则
- 建议25:避免 finally 中可能发生的陷阱
- 建议26:深入理解 None 正确判断对象是否为空
- 建议27:连接字符串应优先使用 join 而不是 +
- 建议28:格式化字符串时尽量使用 .format 方式而不是 %
- 建议29:区别对待可变对象和不可变对象
- 建议30:[]、() 和 {}:一致的容器初始化形式
- 建议31:记住函数传参既不是传值也不是传引用
- 建议32:警惕默认参数潜在的问题
- 建议33:慎用变长参数
- 建议34:深入理解 str() 和 repr() 的区别
- 建议35:分清 staticmethod 和 classmethod 的适用场景
- 第4章 库
- 建议36:掌握字符串的基本用法
- 建议37:按需选择 sort() 或者 sorted()
- 建议38:使用 copy 模块深拷贝对象
- 建议39:使用 Counter 进行计数统计
- 建议40:深入掌握 ConfigParser
- 建议41:使用 argparse 处理命令行参数
- 建议42:使用 pandas 处理大型 CSV 文件
- 建议43:一般情况使用 ElementTree 解析 XML
- 建议44:理解模块 pickle 优劣
- 建议45:序列化的另一个不错的选择 JSON
- 建议46:使用 traceback 获取栈信息
- 建议47:使用 logging 记录日志信息
- 建议48:使用 threading 模块编写多线程程序
- 建议49:使用 Queue 使多线程编程更安全
- 第5章 设计模式
- 第6章 内部机制
- 建议54:理解 built-in objects
- 建议55:init() 不是构造方法
- 建议56:理解名字查找机制
- 建议57:为什么需要 self 参数
- 建议58:理解 MRO 与多继承
- 建议59:理解描述符机制
- 建议60:区别 getattr() 和 getattribute() 方法
- 建议61:使用更为安全的 property
- 建议62:掌握 metaclass
- 建议63:熟悉 Python 对象协议
- 建议64:利用操作符重载实现中缀语法
- 建议65:熟悉 Python 的迭代器协议
- 建议66:熟悉 Python 的生成器
- 建议67:基于生成器的协程及 greenlet
- 建议68:理解 GIL 的局限性
- 建议69:对象的管理与垃圾回收
- 第7章 使用工具辅助项目开发
- 第8章 性能剖析与优化
建议22:使用 with 自动关闭资源
来做个简单的试验,观察一下发生的现象。在Python解释器中输入下面两行代码,会有什么情况发生呢?
>>> f = open('test.txt', 'w') >>> f.write("test")
答案是:在解释器所在的目录下生成了一个文件test.txt,并且在里面写入了字符串test,对吗?事实真相是:的确生成了一个文件,但其内容为空,并没有写入任何字符串。这个一个简单得不能再简单的问题,相信不用多说你已经知道症结所在了。
对文件操作完成后应该立即关闭它们,这是一个常识。我们都知道需要这么做,在很多编程语言中都会强调这个问题,因为打开的文件不仅会占用系统资源,而且可能影响其他程序或者进程的操作,甚至会导致用户期望与实际操作结果不一致。但实际应用中真相往往是:即使我们心中记得这个原则,但仍然可能会忘记关闭它。为什么?因为编程人员会把更多的精力和注意力放在对具体文件内容的操作和处理上;或者设计的正常流程是处理完毕关闭文件,但结果程序执行过程中发生了异常导致关闭文件的代码没有被执行到。也许你会说,还有try..finally块。对!这是一种比较古老的方法,但Python提供了一种更为简单的解决方案:with语句。with语句的语法为:
with 表达式 [ as 目标] : 代码块
with语句支持嵌套,支持多个with子句,它们两者可以相互转换。“with expr1 as e1 , expr2 as e2”与下面的嵌套形式等价:
with expr1 as e1: with expr2 as e2:
with语句的使用非常简单,本节开头的例子改用with语句能够保证当写操作执行完毕后自动关闭文件。
>>> with open('test.txt','w') as f: ... f.write("test") ...
with语句可以在代码块执行完毕后还原进入该代码块时的现场。包含有with语句的代码块的执行过程如下:
1)计算表达式的值,返回一个上下文管理器对象。
2)加载上下文管理器对象的__exit__()方法以备后用。
3)调用上下文管理器对象的__enter__()方法。
4)如果with语句中设置了目标对象,则将__enter__()方法的返回值赋值给目标对象。
5)执行with中的代码块。
6)如果步骤5中代码正常结束,调用上下文管理器对象的_exit__()方法,其返回值直接忽略。
7)如果步骤5中代码执行过程中发生异常,调用上下文管理器对象的_exit__()方法,并将异常类型、值及traceback信息作为参数传递给__exit__()方法。如果_exit__()返回值为false,则异常会被重新抛出;如果其返回值为true,异常被挂起,程序继续执行。
在文件处理时使用with的好处在于无论程序以何种方式跳出with块,总能保证文件被正确关闭。实际上它不仅仅针对文件处理,针对其他情景同样可以实现运行时环境的清理与还原,如多线程编程中的锁对象的管理。with的神奇实际得益于一个称为上下文管理器(context manager)的东西,它用来创建一个运行时的环境。上下文管理器是这样一个对象:它定义程序运行时需要建立的上下文,处理程序的进入和退出,实现了上下文管理协议,即在对象中定义__enter__()和__exit__()方法。其中:
__enter__():进入运行时的上下文,返回运行时上下文相关的对象,with语句中会将这个返回值绑定到目标对象。如上面的例子中会将文件对象本身返回并绑定到目标f。
__exit__(exception_type,exception_value,traceback):退出运行时的上下文,定义在块执行(或终止)之后上下文管理器应该做什么。它可以处理异常、清理现场或者处理with块中语句执行完成之后需要处理的动作。
实际上任何实现了上下文协议的对象都可以称为一个上下文管理器,文件也是实现了这个协议的上下文管理器,它们都能够与with语句兼容。文件对象的__enter__和__exit__属性如下:
>>> f.__enter__ <built-in method __enter__ of file object at 0x029F0700> >>> f.__exit__ <built-in method __exit__ of file object at 0x029F0700>
用户也可以定义自己的上下文管理器来控制程序的运行,只需要实现上下文协议便能够和with语句一起使用。
>>> class MyContextManager(object): ... def __enter__(self):# 实现__enter__ 方法 ... print "entering..." ... def __exit__(self,exception_type, exception_value, traceback): ... print "leaving..." ... if exception_type is None: ... print "no exceptions!" ... return False ... elif exception_type is ValueError: ... print "value error!!!" ... return True ... else: ... print "other error" ... return True ... >>> >>> with MyContextManager(): ... print "Testing..." ... raise(ValueError) ... entering... Testing... leaving... value error!!! >>> >>> with MyContextManager(): ... print "Testing..." ... entering... Testing... leaving... no exceptions! >>>
因为上下文管理器主要作用于资源共享,因此在实际应用中__enter()__和__exit()__方法基本用于资源分配以及释放相关的工作,如打开/关闭文件、异常处理、断开流的连接、锁分配等。为了更好地辅助上下文管理,Python还提供了contextlib模块,该模块是通过Generator实现的,contextlib中的contextmanager作为装饰器来提供一种针对函数级别的上下文管理机制,可以直接作用于函数/对象而不用去关心__enter()__和__exit()__方法的具体实现。关于contextlib更多内容读者可以参考网页http://docs.python.org/2/library/contextlib.html。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论