【Python】file.read()查找源码之旅
目录
一、前言
二、查找过程
三、file.read()源码(来源:项目文件夹\.venv\Lib\site-packages\PIL\ContainerIO.py)
四、后记
五、file.read()源码(来源:Python安装目录\Lib\_pyio.py)(“正主”)
一、前言
在学习Python的过程中,突然想查看读取文件的read()方法的源码,结果在PyCharm编辑器中按住Ctrl+鼠标左键点击read()方法进去查看,发现啥也没有,就一脸懵……就这儿???
@abstractmethod
def read(self, n: int = -1) -> AnyStr:
pass
看了个寂寞……后面查找各种资料也没找到……(emm……官方文档也不太会查,如果有大佬知道的请指教一二,非常感谢!)后面就一顿乱找。
二、查找过程
1、自己写的Test4.py文件里read()方法:
2、按住Ctrl+鼠标左键点击read()方法进去查看其内部代码:
再按住Ctrl+鼠标左键点击read又回到自己写的Test4.py文件里的read()方法了……然后想着,应该是File或IO相关的文件,于是先定位typing.py的位置,再到附近找找。
3、定位typing.py文件并查找附近的File或IO等文件:
filecmp.py:
fileinput.py:
io.py:
很遗憾,都没有……于是陷入了迷茫……后面又找了typing.py里面的IO类:
根据提示按Ctrl+Alt+F7(有时候要按多次才有反应),出来一大包,然后随便找了个比较“可疑”的write()方法:
来到了typing.pyi:
查找了一下read()方法:
点了个小图标出来一大包,当时心想着一个个去看,应该总能找到:
结果第一个就是,哈哈哈哈哈!
三、file.read()源码(来源:项目文件夹\.venv\Lib\site-packages\PIL\ContainerIO.py)
位置:D:\Projects\code\Python\Study\Test\.venv\Lib\site-packages\PIL\ContainerIO.py
(D:\Projects\code\Python\Study\Test是我的项目文件)
def read(self, n: int = -1) -> AnyStr:
"""
Read data.
:param n: Number of bytes to read. If omitted, zero or negative,
read until end of region.
:returns: An 8-bit string.
"""
if n > 0:
n = min(n, self.length - self.pos)
else:
n = self.length - self.pos
if n <= 0: # EOF
return b"" if "b" in self.fh.mode else "" # type: ignore[return-value]
self.pos = self.pos + n
return self.fh.read(n)
四、后记
不是,谁能想到还有个typing.pyi文件,而且typing.py和typing.pyi这俩哥们儿根本不在一块儿,不在同一个包……
D:\ProgramFiles\Python\Python311\Lib\typing.py
D:\ProgramFiles\JetBrains\PyCharm2024.3.1.1\plugins\python-ce\helpers\typeshed\stdlib\typing.pyi
咱也不知道咋回事儿,总之是找到了源码o(* ̄▽ ̄*)ブ
但是!还没结束!怀着一颗好奇的心,查了一下,ContainerIO.py里面read()方法的源码是图像处理的PIL这个库的,估计之前导入过相关的库,我重新创建一个项目后,直接写读取文件代码,不干其他的,结果就没有ContainerIO.py这玩意儿了,所以说这个源码可能并不准确!
神奇的事情来了!在通过我Ctrl+H全局一顿胡乱搜索read()方法之后,再通过Ctrl+鼠标左键点击typing.py里的read()方法,结果弹出来一大包东西。(?_?)
这里可以选择“Project Files”,点击一下就回到我们自己的项目文件夹里了。之前这里应该默认Project Files,所以查找起来才这么辛苦……
然后找到了第二个源码。
五、file.read()源码(来源:Python安装目录\Lib\_pyio.py)(“正主”)
D:\ProgramFiles\Python\Python311\Lib\_pyio.py
read()和_read_unlocked()方法的源码:
def read(self, size=None):
"""Read size bytes.
Returns exactly size bytes of data unless the underlying raw IO
stream reaches EOF or if the call would block in non-blocking
mode. If size is negative, read until EOF or until read() would
block.
"""
if size is not None and size < -1:
raise ValueError("invalid number of bytes to read")
with self._read_lock:
return self._read_unlocked(size)
def _read_unlocked(self, n=None):
nodata_val = b""
empty_values = (b"", None)
buf = self._read_buf
pos = self._read_pos
# Special case for when the number of bytes to read is unspecified.
if n is None or n == -1:
self._reset_read_buf()
if hasattr(self.raw, 'readall'):
chunk = self.raw.readall()
if chunk is None:
return buf[pos:] or None
else:
return buf[pos:] + chunk
chunks = [buf[pos:]] # Strip the consumed bytes.
current_size = 0
while True:
# Read until EOF or until read() would block.
chunk = self.raw.read()
if chunk in empty_values:
nodata_val = chunk
break
current_size += len(chunk)
chunks.append(chunk)
return b"".join(chunks) or nodata_val
# The number of bytes to read is specified, return at most n bytes.
avail = len(buf) - pos # Length of the available buffered data.
if n <= avail:
# Fast path: the data to read is fully buffered.
self._read_pos += n
return buf[pos:pos+n]
# Slow path: read from the stream until enough bytes are read,
# or until an EOF occurs or until read() would block.
chunks = [buf[pos:]]
wanted = max(self.buffer_size, n)
while avail < n:
chunk = self.raw.read(wanted)
if chunk in empty_values:
nodata_val = chunk
break
avail += len(chunk)
chunks.append(chunk)
# n is more than avail only when an EOF occurred or when
# read() would have blocked.
n = min(n, avail)
out = b"".join(chunks)
self._read_buf = out[n:] # Save the extra data in the buffer.
self._read_pos = 0
return out[:n] if out else nodata_val
结案!0(n_n)0
(emm……其实在定位typing.py文件并查找附近的File或IO等文件的时候就很接近真相了,结果走岔路走了😂再往上找找,找到_pyio.py就哦了)
作者:晨曦之光Wing