【问题标题】:Is it safe to open a file several times at once in Python?在 Python 中一次打开一个文件是否安全?
【发布时间】:2013-01-24 20:58:30
【问题描述】:

我似乎记得在较低级别的语言中,在程序中多次打开文件可能会导致共享查找指针。通过在 Python 中搞乱一点,这对我来说似乎没有发生:

$ cat file.txt
first line!
second
third
fourth
and fifth
>>> f1 = open('file.txt')
>>> f2 = open('file.txt')
>>> f1.readline()
'first line!\n'
>>> f2.read()
'first line!\nsecond\nthird\nfourth\nand fifth\n'
>>> f1.readline()
'second\n'
>>> f2.read()
''
>>> f2.seek(0)
>>> f1.readline()
'third\n'

这种行为是否安全?我很难找到一个消息来源说没关系,如果我能依赖这个,那将有很大帮助。

我没有将位置视为文件对象的属性,否则我对此更有信心。我知道它可以保存在迭代器内部,但不知道在这种情况下 .tell() 将如何获取它。

>>> dir(f1)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
 '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
 '__setattr__', '__str__', 'close', 'closed', 'encoding', 'fileno', 'flush',
 'isatty', 'mode', 'name', 'newlines', 'next', 'read', 'readinto', 'readline',
 'readlines', 'seek', 'softspace', 'tell', 'truncate', 'write', 'writelines',
 'xreadlines']

更新
The Python Essential Reference 的第 161 页上声明

同一个文件可以在同一个程序中多次打开(或在 不同的程序)。打开文件的每个实例都有自己的 可以独立操作的文件指针。

所以它实际上似乎是安全的、已定义的行为

【问题讨论】:

  • 在python中每次调用open()它都会创建一个新的文件对象(迭代器),所以你很安全。
  • 我不知道有任何平台在这里维护不同的查找指针可能会遇到问题。但是……对于您的用例来说,您的代码可能会在某些情况下在 Windows 上引发打开 f2 的异常,即使它在 Unix 上永远不会失败,这是否可以接受?
  • @abarnert 我只希望它能够在 Windows 上运行,并且仅在阅读时运行。在windows上打开会正常获取排他锁吗?
  • @xhainingx:IIRC,它不会获取任何锁,这意味着只要没有其他代码获取文件的任何锁,就可以了。但是,我实际上无法在任何地方找到记录。 fopen 根本没有提到分享。如果你往里面看,它只是用_SH_DENYNO 调用fsopen(至少在VC7 和VC10 中),但似乎没有任何东西可以保证任何地方,或者这里的shflagCreateFile 中的dwShareMode 交互方式你希望……
  • @xhainingx:在 Unix 上,它要简单得多。 POSIX 定义了事情是如何工作的,基本上,即使特定的 Unix 确实具有 Windows 样式的锁定,也不允许在这里使用它。就像你可以在 4 个不同的终端中 tail -f foo 并且它们不会相互干扰一样,你可以在 2 个线程中打开文件并读取它们,它们不会相互干扰。

标签: python file seek


【解决方案1】:

在现代操作系统上(1969 年后用于类 UNIX 操作系统,或 2000 年后用于 Windows,可能在此之前,但我将 Win2K 视为第一个“现代”Windows),打开文件的每个实例 (文件描述符)有自己的查找指针。 Python 的file 类没有魔法可以导致实例共享状态; file是一个普通C文件句柄的封装,它本身封装了一个OS文件描述符,file.tell()file.seek()的实现调用对应的Cstdio函数。 (有关混乱的细节,请参阅 CPython 的 fileobject.c。)C 库行为和底层操作系统的行为之间可能存在差异,但在这种特殊情况下,这不是一个因素。

如果您使用 IronPython 或 Jython,它将使用标准 .Net 或 Java 文件对象作为其底层实现,而后者又将使用标准 C 库或操作系统实现。

所以你的方法很好,除非你以某种方式在一些具有奇怪 I/O 行为的非标准操作系统上运行 Python。

如果不及时刷新,可能会在写的时候得到意想不到的结果;数据在实际到达磁盘之前可能会在内存中停留一段时间,并且可供您在同一文件上打开的其他文件描述符使用。正如 abarnert 在评论中指出的那样,这无论如何都是有问题的,除非在非常简单的情况下。

【讨论】:

  • 如果您以只读方式打开所有文件,则永远不会出现“刷新”问题。如果您以只读方式打开所有文件,您将遇到更严重的问题。
  • 更重要的是,这里的大部分细节都是错误的。 file 不是普通文件描述符的包装器;它是FILE * 的包装器。并且tellseek 不会直接调用相应的低级操作系统函数;他们调用相应的中级stdio 函数。这在 Windows 上产生了很大的不同,因为 fopenCreateFile 中文件锁定的工作方式完全不同。
猜你喜欢
  • 1970-01-01
  • 2014-01-06
  • 1970-01-01
  • 1970-01-01
  • 2019-02-03
  • 1970-01-01
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
相关资源
最近更新 更多