我不打算浪费你的时间,但是:你也发现了它,而使用Python的with
声明它确实违反了“禅宗的Python”的第5行“那就是”Flat比嵌套好“?任何开明的Python专家能否与我分享他们对此的一些看法?
(我总是发现每次使用with
代替f.close()
时,我的代码中会再出现一个缩进级别...而且我不会使用{{1无论如何,try: ... finally: ...
的好处仍然让我望而却步,即使我越来越喜欢和理解Python ......)
@glglgl(抱歉,我找不到在评论中编写代码的方法):
是的,但是如果你采用with
方式,你的代码将成为:
with
...并且在没有try:
with file(...) as f:
...
except IOError:
...
的情况下使用只是人们最终在hacky“一次使用”代码中使用try
而不是反正使用(这很糟糕因为如果在f.close()
之前抛出异常,该文件可能无法关闭,因此对于“hacky”代码,人们只是不使用f.close()
,因为我不知道,我猜他们只是找到它太“花哨”了,对于结构良好的代码来说它无论如何都没有带来任何好处,所以在我看来还没有真正的世界用例......这是我对此的思考。
答案 0 :(得分:7)
你已经提到过了:做起来比较干净
f = file(...)
try:
# do work on file
finally:
f.close()
而不仅仅是在文件操作之后关闭 - 如果发生异常将无法达到。
如果您将try/finally
与with
进行比较,则会有相同级别的缩进,因此您不会丢失任何内容。但是,如果你进行异常处理,你会有一个更多级别的缩进,这确实违背了上述Zen点。
OTOH,with
封装了东西,使得使用它们更容易,更易读,这是其他禅宗方面。
我似乎不可能始终完全遵循每一个禅宗方面;有时你必须权衡一个与另一个。在这种情况下,您“失去”一级缩进,但您可以获得更好的可读性和可维护性。后者似乎对我有利。
答案 1 :(得分:7)
请注意,Python的Zen也说:
简单比复杂更好。
复杂比复杂更好。
和
可读性计数。
在with
语句中使用上下文管理器提供了多种功能:
with open(..) as f
非常容易理解)你不能指向Python的Zen中的一个项目,并且认为所有Python代码必须始终满足所有项目。例如,如果以可读和正确的方式解决特定问题的最小缩进级别为4,那么就是这样:如果缩进级别为3使代码可读性降低,则只留下代码(四个是好的)。
答案 2 :(得分:5)
是的,The Zen of Python声明“Flat优于嵌套”,但它并不是我们关心的唯一特征;它还指出“简单比复杂更好”。 with
的美妙之处在于它实际上遵循了这些原则的两者,我将在下面解释。
每当你发现自己对Python中的一个特性进行哲学思考时,可能需要查阅Python Enhancement Proposals (PEPs)以了解该功能背后的动机。在这种情况下,PEP 343 -- The "with" Statement在摘要中预先说明了这一点:
这个PEP为Python语言添加了一个新的声明“with” 可以分解try / finally语句的标准用法。
分析try/finally
语句使代码更简单,更易读。
PEP 343比提供一些简单的语法糖更深入。它建立了一个上下文管理器协议:
语句中紧跟在with关键字之后的表达式 是一个“上下文表达式”,因为该表达式提供了主要线索 上下文管理器为运行时环境建立的 声明正文的持续时间。
使用上下文管理器协议,API编写器可以帮助隐藏复杂性并确保在多线程上下文中正确获取/释放资源。
但PEP 343的示例12中显示了with
语句的真正美丽,它解释了:
“嵌套”上下文管理器,自动嵌套提供的内容 从左到右的背景,以避免过度缩进。
使用nested()
上下文管理器,您可以获取如下代码:
with a as x:
with b as y:
with c as z:
# Perform operation
并将其转换为:
with nested(a, b, c) as (x, y, z):
# Perform operation
请注意,nested()
是在Python 2.5中引入的,但是从版本2.7开始,不赞成使用这种多语境管理器语法形式:
with a as x, b as y, c as z:
# Perform operation
显然,这不仅更简单,更易读,而且比嵌套更扁平。因此,使用with
遵循無爲 :)的路径
更新:在回复comments on Simeon Visser's answer时,您可以使用多个上下文管理器同时打开多个文件,当您要压缩两个内容时(或更多)文件一起使得如果打开其中一个文件失败,它将使整个事件失败并正确关闭所打开的每个文件:
from itertools import izip
with open("/etc/passwd") as a, open("/etc/group") as b, open("/etc/shadow") as c:
for lines in izip(a,b,c):
print map(lambda x: x.split(':')[0], lines)
运行此示例两次;一次作为root,一次作为普通用户。假设您将此文件保存为ziptogether.py
,首先尝试以sudo python ziptogether.py
作为root用户调用它并且它会成功,但是以python ziptogether.py
作为普通用户调用它将失败,因为您没有权限阅读/etc/shadow
。当失败时,上下文管理器将确保在执行超出with
语句范围时正确关闭在失败之前成功打开的文件。
答案 3 :(得分:1)
"Flat is better than nested"
嗯,什么是持平?
import thirdparty
print "I Like Pie!"
VS
import org.example.thirdparty.something
System.out.println("I Like Cake")
等...
Python的Zen不会简单地对代码强制执行缩进限制。它鼓励您编写可读(因而更好)的代码。
如果您的with
语句位于只能由3层对象(等,one.two.three.func()
)访问的函数中,那么这是一个问题。
否则,三个缩进级别与任何级别一样好。
答案 4 :(得分:1)
偏好with
的原因是您不需要手动配对 相关操作(例如open(...)
/ .close()
;但是{{ 1}}构造更通用 - 不仅用于处理文件)。这是重要的,即在由于源代码中不清楚可见的原因而可能不执行第二操作的情况下。你告诉机器为我照顾它,机器比人类更好。通过这种方式,您可以摆脱可能难以找到的一组令人讨厌的错误。
顺便说一句,您应该使用with
而不是open(...)
。 Python 3对file(...)
一无所知,否则您将不得不在以后修复代码。