首先看FileProxyMixin的定義:python
class FileProxyMixin(object): encoding = property(lambda self: self.file.encoding) fileno = property(lambda self: self.file.fileno) flush = property(lambda self: self.file.flush) isatty = property(lambda self: self.file.isatty) newlines = property(lambda self: self.file.newlines) read = property(lambda self: self.file.read) readinto = property(lambda self: self.file.readinto) readline = property(lambda self: self.file.readline) readlines = property(lambda self: self.file.readlines) seek = property(lambda self: self.file.seek) softspace = property(lambda self: self.file.softspace) tell = property(lambda self: self.file.tell) truncate = property(lambda self: self.file.truncate) write = property(lambda self: self.file.write) writelines = property(lambda self: self.file.writelines) xreadlines = property(lambda self: self.file.xreadlines) def __iter__(self): return iter(self.file)
能夠看到使用property屬性裝飾器和lambda函數,完成對self.file大部分操做的代理,而且也實現__iter__方法,支持迭代。函數
而後看File的定義,它的做用主要是完成對不一樣的類型的文件的抽象。增長chunks方法,用於分片讀取數據大的文件,增長closed的屬性,判斷是否文件關閉, 增長size屬性。this
首先看size屬性的實現函數:spa
def _get_size_from_underlying_file(self): if hasattr(self.file, 'size'): return self.file.size if hasattr(self.file, 'name'): try: return os.path.getsize(self.file.name) except (OSError, TypeError): pass if hasattr(self.file, 'tell') and hasattr(self.file, 'seek'): pos = self.file.tell() self.file.seek(0, os.SEEK_END) size = self.file.tell() self.file.seek(pos) return size raise AttributeError("Unable to determine the file's size.")
首先嚐試獲取self.file的size屬性, 而後嘗試獲取本地文件的大小,最後使用seek方法獲取大小。代理
self.file.seek(0, os.SEEK_END) size = self.file.tell()
接下來看看File如何支持迭代的:code
def __iter__(self): # Iterate over this file-like object by newlines buffer_ = None for chunk in self.chunks(): for line in chunk.splitlines(True): if buffer_: if endswith_cr(buffer_) and not equals_lf(line): # Line split after a \r newline; yield buffer_. yield buffer_ # Continue with line. else: # Line either split without a newline (line # continues after buffer_) or with \r\n # newline (line == b'\n'). line = buffer_ + line # buffer_ handled, clear it. buffer_ = None # If this is the end of a \n or \r\n line, yield. if endswith_lf(line): yield line else: buffer_ = line if buffer_ is not None: yield buffer_
這裏迭代的邏輯,主要是buffer_的使用。get
首先會調用chunks進行塊讀取,而後將line進行分行(splitlines)。將每段line剩下的字節,保存在buffer_中,等待下段一塊兒合併。it