跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • Python 读写文件时不用 with 也不关闭文件,这样的风格好吗?
未分類
17 3 月 2021

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

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

資深大佬 : zictos 0

读文件:

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   

大佬有話說 (39)

  • 資深大佬 : ClericPy

    Explicit is better than implicit.

  • 資深大佬 : learningman

    都有上下文管理器这么好用的东西了,你还嫌麻烦。。。
    要不再封装成一个函数,传入文件名返回值,内部做好处理。

  • 資深大佬 : AndyAO

    临时读一下文件没必要关闭,就那点资源根本就没必要释放

  • 資深大佬 : v2sir

    f = 1
    触发了 python 回收机制

  • 資深大佬 : ljpCN

    得看下 f.close 到底做了什么,直觉上感觉不调用会内存泄漏。另外,更想知道的是用 with 和手动调 close 哪个是更好的实践呢?前者少写了代码省心了但多了一层缩进。如果要在过程中打开多个文件就缩得更多。

  • 資深大佬 : geelaw

    这样的写法在标准 Python 中可以导致文件在进程结束前不被关闭,因为在内存充足的时候,垃圾回收器可以选择完全不回收不可达对象。

    在目前的 CPython 实现中,这种写法可以保证文件立刻被关闭,因为 CPython 采用引用计数来辅助管理内存,引用计数到达 0 的时候会立刻回收,从而关闭文件。

    在 IronPython 中,则不一定,现在 CLR 提供要求暂时不运行 GC 的 API,如果在运行这段代码前,有人 commit 了足够的内存要求不运行 GC,且这段代码之后程序不再分配内存,则文件永远不会被关闭。

    不要面向巧合编程。

  • 資深大佬 : GrayXu

    取决于场景,如果不需要在某个时刻保证资源被释放,那隐式完成其实也可以吧。。

  • 資深大佬 : lqf96

    @geelaw 都不需要 IronPython,PyPy 就已经火葬场了…

  • 主 資深大佬 : zictos

    如果仅仅是用 open(‘test.txt’, ‘r’, encoding=’utf-8′)打开文件,但没有做任何操作,发现 test.txt 文件是无法删除的。但只要做了别的操作或者一开始赋值给了变量后续又改变了变量的数据类型就可以删除了。

  • 主 資深大佬 : zictos

    @ljpCN #5 问题在于文件能够删除了就肯定说明内存已经释放了
    有时候我反倒不想看到 with 的缩进,因为我就是读取一下文件,本来也一两行代码就可以搞定。只是用 with 的确不容易忘记 close,效果应该是一样的。

  • 資深大佬 : Contextualist

    如果只是一次性读 / 写,建议使用 pathlib:
    from pathlib import Path
    Path(‘test.txt’).write_text(‘123′, encoding=’utf-8’)
    text = Path(‘test.txt’).read_text(encoding=’utf-8′)

    这些方法都是自带关闭文件的。

  • 資深大佬 : zhuangzhuang1988

    接手的人 看了要流泪

  • 資深大佬 : webshe11

    @lqf96 #8 试了一下,确实,macOS 下 lsof 发现 PyPy 7.3.3 不会自动关闭,CPython 3.9.2 2.7.17 都会自动关闭文件
    个人觉得就写个 with 再缩进一下不算麻烦,如果实在嫌麻烦可以封装个 file_get_contents() 函数(滑稽)

  • 主 資深大佬 : zictos

    @webshe11 #13 那看来还是跟解释器有关,兼容型不是很好。不过自己使用倒是没问题。
    因为我的需求也重点是用在小脚本,比如有时候打算写一个本来就只有十几行的脚本,或者在安卓手机上用 termux 运行 python,这样比较方便。

  • 資深大佬 : kikikiabc

    > 要不再封装成一个函数,传入文件名返回值,内部做好处理?

    有,自带的 pathlib 已经封装好了的,确实是“传入文件名返回值”, 很好用

  • 資深大佬 : hxy100

    @webshe11 PHP 乱入?

  • 資深大佬 : xiaolinjia

    在流畅的 python 一书中有提到:
    CPython 中的垃圾回收主要依靠引用计数,这容易实现,但是遇到引用循环容易泄露
    内存,因此 CPython 2.0 ( 2000 年 10 月发布)实现了分代垃圾回收程序,它能把引用
    循环中不可获取的对象销毁。
    但是引用计数仍然作为一种基准存在,一旦引用数量归零,就立即销毁对象。这意味
    着,在 CPython 中,这样写是安全的(至少目前如此):
    open(‘test.txt’, ‘wt’, encoding=’utf-8′).write(‘1, 2, 3’)
    这行代码是安全的,因为文件对象的引用数量会在 write 方法返回后归零,Python
    在销毁内存中表示文件的对象之前,会立即关闭文件。然而,这行代码在 Jython 或
    IronPython 中却不安全,因为它们使用的是宿主运行时( Java VM 和 .NET CLR )中的
    垃圾回收程序,那些回收程序更复杂,但是不依靠引用计数,而且销毁对象和关闭文
    件的时间可能更长。在任何情况下,包括 CPython,最好显式关闭文件;而关闭文件
    的最可靠方式是使用 with 语句,它能保证文件一定会被关闭,即使打开文件时抛出
    了异常也无妨。

  • 資深大佬 : AndyAO

    单纯是为了方便的话可以用 With 来写个 Snippet

  • 資深大佬 : johnsona

    @v2sir 好家伙 你这垃圾回收机制都学以致用了

  • 資深大佬 : imn1

    “老子写的程序绝对没有 bug,也绝对不会耗时,锁文件又如何,爱咋咋地”

  • 資深大佬 : ipwx

    主已经有答案了,他承认不符合语言标准,但是就是爱这么干。你们回帖有啥意义?散了散了。。。

  • 主 資深大佬 : zictos

    @Contextualist #11 pathlib 能直接遍历每一行(针对大文件)吗?

  • 主 資深大佬 : zictos

    @ipwx #21 其实如果没有其他坑的话这样用也没问题,但目前看来是有坑的。只是我不发这个帖子的话我以为不会有坑的

  • 資深大佬 : ipwx

    @zictos 想要遍历每一行又能确保关闭文件,你自己写个函数呗:

    def iter_lines(path, encoding=None):
    ….f = open(path, ‘r’, encoding=encoding)
    ….try:
    ……..for line in f:
    …………yield line
    ….finally:
    ……..f.close()

  • 主 資深大佬 : zictos

    @ipwx #24 只是要保证随时能用,不会每次都去写函数。如果放到 python 第三方模块的文件夹的话每次使用时引入也可以,只是不能保证去任何有 python 环境的电脑都能用。

  • 資深大佬 : ipwx

    @zictos 要求真多。with 不过多一行。问就是标准库没有

  • 資深大佬 : abersheeran

    @zictos 上说的 pathlib 就是标准库。任何 Python3 的环境你都可以用。

  • 主 資深大佬 : zictos

    @ipwx #26 嗯,with 也没多麻烦,用其他途径去弄最后可能比 with 还麻烦。
    只是看到有人推荐 pathlib,而我题中也说了遍历的例子,所以才随口问一下 pathlib 是否能遍历而已,也不是一定要用 pathlib 遍历

  • 主 資深大佬 : zictos

    @abersheeran #27 嗯,但 pathlib 还是不一样的,不能直接遍历。读取或写入倒是没问题,以后读取或写入可以选择用 pathlib

  • 資深大佬 : crclz

    如果你嫌 with 多了一层缩进的话,C#的 using 很满足你的需求。using var reader = xxx;

  • 資深大佬 : laike9m

    这个问题还有争议也是我没想到的。。

  • 資深大佬 : chenqh

    @crclz python 的 with,不就相当于 C#的 using(){}吗?

  • 資深大佬 : mrchi

    @ljpCN 多个文件不必多层缩进,参考文档: https://docs.python.org/3/reference/compound_stmts.html#the-with-statement

    with A() as a, B() as b:
    SUITE

  • 資深大佬 : Contextualist

    @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

    @Contextualist 你的认知是错误的。

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

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

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

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

    with my_context():
    ….raise Error(…)

  • 資深大佬 : Contextualist

    @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

    @Contextualist Oh 多谢提示,我还没注意过 .throw()。主要是我自己写自己的库一般用 .close() 来处理 for iter ….,所以也不会受引用计数的 gc 限制。

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

    看了看 .close() 八成等于 .throw(StopIteration)

  • 資深大佬 : geelaw

    @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

    @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 …: 的语法

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具