读文件:
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
1
ClericPy 2021-03-27 12:19:59 +08:00 1
Explicit is better than implicit.
|
2
learningman 2021-03-27 12:21:20 +08:00 via Android
都有上下文管理器这么好用的东西了,你还嫌麻烦。。。
要不再封装成一个函数,传入文件名返回值,内部做好处理。 |
3
AndyAO 2021-03-27 12:22:21 +08:00
临时读一下文件没必要关闭,就那点资源根本就没必要释放
|
4
v2sir 2021-03-27 12:22:46 +08:00
f = 1
触发了 python 回收机制 |
5
ljpCN 2021-03-27 12:24:41 +08:00 via iPhone
得看下 f.close 到底做了什么,直觉上感觉不调用会内存泄漏。另外,更想知道的是用 with 和手动调 close 哪个是更好的实践呢?前者少写了代码省心了但多了一层缩进。如果要在过程中打开多个文件就缩得更多。
|
6
geelaw 2021-03-27 12:25:17 +08:00 via iPhone 27
这样的写法在标准 Python 中可以导致文件在进程结束前不被关闭,因为在内存充足的时候,垃圾回收器可以选择完全不回收不可达对象。
在目前的 CPython 实现中,这种写法可以保证文件立刻被关闭,因为 CPython 采用引用计数来辅助管理内存,引用计数到达 0 的时候会立刻回收,从而关闭文件。 在 IronPython 中,则不一定,现在 CLR 提供要求暂时不运行 GC 的 API,如果在运行这段代码前,有人 commit 了足够的内存要求不运行 GC,且这段代码之后程序不再分配内存,则文件永远不会被关闭。 不要面向巧合编程。 |
7
GrayXu 2021-03-27 12:25:43 +08:00
取决于场景,如果不需要在某个时刻保证资源被释放,那隐式完成其实也可以吧。。
|
9
zictos OP 如果仅仅是用 open('test.txt', 'r', encoding='utf-8')打开文件,但没有做任何操作,发现 test.txt 文件是无法删除的。但只要做了别的操作或者一开始赋值给了变量后续又改变了变量的数据类型就可以删除了。
|
10
zictos OP @ljpCN #5 问题在于文件能够删除了就肯定说明内存已经释放了
有时候我反倒不想看到 with 的缩进,因为我就是读取一下文件,本来也一两行代码就可以搞定。只是用 with 的确不容易忘记 close,效果应该是一样的。 |
11
Contextualist 2021-03-27 12:41:39 +08:00 3
如果只是一次性读 / 写,建议使用 pathlib:
from pathlib import Path Path('test.txt').write_text('123', encoding='utf-8') text = Path('test.txt').read_text(encoding='utf-8') 这些方法都是自带关闭文件的。 |
12
zhuangzhuang1988 2021-03-27 12:42:05 +08:00
接手的人 看了要流泪
|
13
webshe11 2021-03-27 12:42:14 +08:00
@lqf96 #8 试了一下,确实,macOS 下 lsof 发现 PyPy 7.3.3 不会自动关闭,CPython 3.9.2 2.7.17 都会自动关闭文件
个人觉得就写个 with 再缩进一下不算麻烦,如果实在嫌麻烦可以封装个 file_get_contents() 函数(滑稽) |
14
zictos OP @webshe11 #13 那看来还是跟解释器有关,兼容型不是很好。不过自己使用倒是没问题。
因为我的需求也重点是用在小脚本,比如有时候打算写一个本来就只有十几行的脚本,或者在安卓手机上用 termux 运行 python,这样比较方便。 |
15
kikikiabc 2021-03-27 12:46:51 +08:00 via iPhone
> 要不再封装成一个函数,传入文件名返回值,内部做好处理?
有,自带的 pathlib 已经封装好了的,确实是“传入文件名返回值”, 很好用 |
17
xiaolinjia 2021-03-27 12:58:03 +08:00 1
在流畅的 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 语句,它能保证文件一定会被关闭,即使打开文件时抛出 了异常也无妨。 |
18
AndyAO 2021-03-27 13:11:17 +08:00
单纯是为了方便的话可以用 With 来写个 Snippet
|
20
imn1 2021-03-27 13:46:03 +08:00
“老子写的程序绝对没有 bug,也绝对不会耗时,锁文件又如何,爱咋咋地”
|
21
ipwx 2021-03-27 13:49:00 +08:00
楼主已经有答案了,他承认不符合语言标准,但是就是爱这么干。你们回帖有啥意义?散了散了。。。
|
22
zictos OP @Contextualist #11 pathlib 能直接遍历每一行(针对大文件)吗?
|
24
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() |
25
zictos OP @ipwx #24 只是要保证随时能用,不会每次都去写函数。如果放到 python 第三方模块的文件夹的话每次使用时引入也可以,只是不能保证去任何有 python 环境的电脑都能用。
|
27
abersheeran 2021-03-27 14:12:33 +08:00
@zictos 楼上说的 pathlib 就是标准库。任何 Python3 的环境你都可以用。
|
28
zictos OP @ipwx #26 嗯,with 也没多麻烦,用其他途径去弄最后可能比 with 还麻烦。
只是看到有人推荐 pathlib,而我题中也说了遍历的例子,所以才随口问一下 pathlib 是否能遍历而已,也不是一定要用 pathlib 遍历 |
29
zictos OP @abersheeran #27 嗯,但 pathlib 还是不一样的,不能直接遍历。读取或写入倒是没问题,以后读取或写入可以选择用 pathlib
|
30
crclz 2021-03-27 16:08:52 +08:00
如果你嫌 with 多了一层缩进的话,C#的 using 很满足你的需求。using var reader = xxx;
|
31
laike9m 2021-03-27 16:52:52 +08:00 via Android
这个问题还有争议也是我没想到的。。
|
33
mrchi 2021-03-27 22:41:27 +08:00 1
@ljpCN 多个文件不必多层缩进,参考文档: https://docs.python.org/3/reference/compound_stmts.html#the-with-statement
with A() as a, B() as b: SUITE |
34
Contextualist 2021-03-28 00:06:55 +08:00
|
35
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(...) |
36
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 |
37
ipwx 2021-03-28 12:13:58 +08:00
@Contextualist Oh 多谢提示,我还没注意过 .throw()。主要是我自己写自己的库一般用 .close() 来处理 for iter ....,所以也不会受引用计数的 gc 限制。
看了看 .close() 八成等于 .throw(StopIteration) |
38
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 一样。 |
39
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 ...: 的语法 😄
|