Python 读写文件时不用 with 也不关闭文件,这样的风格好吗?

2021-03-27 12:13:58 +08:00
 zictos

读文件:

text = open('test.txt', 'r', encoding='utf-8').read()

写文件:

open('test.txt', 'w', encoding='utf-8').write('123')

遍历文件:

for i in open('test.txt', 'r', encoding='utf-8'):
    print(i)

.
.

发现有时候只是临时读一下文件的话这样方便不少。

如果打开文件,但没有把文件对象赋值到变量的话是不需要关闭文件的
比如输入下面一行代码的时候去删除 test.txt 文件时无法删除,提示被占用

f = open('test.txt', 'r', encoding='utf-8')  

但实际上并不需要输入 f.close(),比如直接给 f 赋值成别的数据类型 f=1,再去删除文件就不会提示占用了

f = 1  
4872 次点击
所在节点    Python
39 条回复
ipwx
2021-03-27 13:49:00 +08:00
楼主已经有答案了,他承认不符合语言标准,但是就是爱这么干。你们回帖有啥意义?散了散了。。。
zictos
2021-03-27 13:54:55 +08:00
@Contextualist #11 pathlib 能直接遍历每一行(针对大文件)吗?
zictos
2021-03-27 13:58:46 +08:00
@ipwx #21 其实如果没有其他坑的话这样用也没问题,但目前看来是有坑的。只是我不发这个帖子的话我以为不会有坑的
ipwx
2021-03-27 13:59:08 +08:00
@zictos 想要遍历每一行又能确保关闭文件,你自己写个函数呗:

def iter_lines(path, encoding=None):
....f = open(path, 'r', encoding=encoding)
....try:
........for line in f:
............yield line
....finally:
........f.close()
zictos
2021-03-27 14:06:49 +08:00
@ipwx #24 只是要保证随时能用,不会每次都去写函数。如果放到 python 第三方模块的文件夹的话每次使用时引入也可以,只是不能保证去任何有 python 环境的电脑都能用。
ipwx
2021-03-27 14:10:26 +08:00
@zictos 要求真多。with 不过多一行。问就是标准库没有
abersheeran
2021-03-27 14:12:33 +08:00
@zictos 楼上说的 pathlib 就是标准库。任何 Python3 的环境你都可以用。
zictos
2021-03-27 14:15:57 +08:00
@ipwx #26 嗯,with 也没多麻烦,用其他途径去弄最后可能比 with 还麻烦。
只是看到有人推荐 pathlib,而我题中也说了遍历的例子,所以才随口问一下 pathlib 是否能遍历而已,也不是一定要用 pathlib 遍历
zictos
2021-03-27 14:18:46 +08:00
@abersheeran #27 嗯,但 pathlib 还是不一样的,不能直接遍历。读取或写入倒是没问题,以后读取或写入可以选择用 pathlib
crclz
2021-03-27 16:08:52 +08:00
如果你嫌 with 多了一层缩进的话,C#的 using 很满足你的需求。using var reader = xxx;
laike9m
2021-03-27 16:52:52 +08:00
这个问题还有争议也是我没想到的。。
chenqh
2021-03-27 17:42:57 +08:00
@crclz python 的 with,不就相当于 C#的 using(){}吗?
mrchi
2021-03-27 22:41:27 +08:00
@ljpCN 多个文件不必多层缩进,参考文档: https://docs.python.org/3/reference/compound_stmts.html#the-with-statement

with A() as a, B() as b:
SUITE
Contextualist
2021-03-28 00:06:55 +08:00
@zictos #22 pathlib 不会提供像 @ipwx #24 描述的文件迭代器。考虑一下下面的代码:
try:
for l in iter_lines('test.txt'):
print(l[100])
except:
pass

如果 for 循环里的语句抛出了异常,程序会继续执行,但是因为 iter_lines 生成器没有执行完,里面的文件不会被关闭。问题的根本原因是:没有办法能 自动 将外层的异常传递进入内层。这就是上下文管理器 / with 语句设计要解决的问题之一。用 with open,如果 for 循环里的语句抛出了异常导致跳出了 with 的范围,文件会被自动关闭。
ipwx
2021-03-28 01:23:01 +08:00
@Contextualist 你的认知是错误的。

(Python 3.7.3)

看到输出的那行 finally 了么?
----

事实上一个自定义的 with contextmanager 就得通过这种方法写,譬如:

@contextlib.contextmanager
def my_context():
....try:
........yield
....finally:
........do some cleanup

with my_context():
....raise Error(...)
Contextualist
2021-03-28 02:17:50 +08:00
@ipwx 啊,感谢提出!仔细研究了一下,这个情况其实也是出于对 gc 机制的利用。如果你试着把这个生成器实例赋给一个变量(避免 gc ),然后再调用它,像这样:
it = iter_lines([1,2,3])
for l in it:
  if l == 2:
     raise ValueError('xxx')

finally 就不会被触发。你的例子中,finally 执行的原因是生成器实例在 for 循环后被 gc 了,Python 的实现要求没执行完的生成器被 gc 时必须执行 finally,详见 https://docs.python.org/3/reference/expressions.html#yieldexpr

至于 contextlib.contextmanager,那是因为 Python 的库将 with 捕获的异常手动传入了生成器,详见 https://github.com/python/cpython/blob/7990072999b7e9b4ef6b1f6bb376d441a5a41d74/Lib/contextlib.py#L135
ipwx
2021-03-28 12:13:58 +08:00
@Contextualist Oh 多谢提示,我还没注意过 .throw()。主要是我自己写自己的库一般用 .close() 来处理 for iter ....,所以也不会受引用计数的 gc 限制。



看了看 .close() 八成等于 .throw(StopIteration)
geelaw
2021-03-29 20:38:01 +08:00
@Contextualist #36
@ipwx #37

我想象中的

for A in B:
..body

的展开是

TMP = B.__iter__() # 编译器生成的迭代器的 __exit__ 会执行剩余的 finally
with TMP:
..while True:
....A = TMP.__next__()
....body

即和 C# 里的 foreach 展开包含 finally IDisposable.Dispose 一样。
Contextualist
2021-03-30 05:17:48 +08:00
@geelaw 虽然现在的 Python 里并没有这样的语法,但是其实 with 上下文里用 for 循环(即显式指定任何情况下跳出循环都需要 clean up )还的确是个常见的模式(例如 Trio 里的 channel: https://trio.readthedocs.io/en/stable/reference-core.html#clean-shutdown-with-channels )。如果真的是常见模式并且不考虑将语法变得冗杂,不妨提议 Python 加个 for ... in with ...: 的语法 😄

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/765647

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX