MediaCrawler:构建企业级社交媒体数据采集架构的技术突破与实践
2026/6/17 3:59:08
len()函数的底层原理并不是简单计数,而是通过不同对象的__len__()方法来实现的。
对于不同的数据类型,底层实现机制完全不同。
len()的通用原理# len() 实际调用的是对象的 __len__() 方法s="hello"print(len(s))# 等价于 s.__len__()lst=[1,2,3]print(len(lst))# 等价于 lst.__len__()# 验证print(len("hello")=="hello".__len__())# True# Python 3 字符串:Unicode 码点序列text="Hello 你好 🎉"print(len(text))# 10# 底层实现(概念性)classPyUnicodeObject:"""Python 字符串对象的简化表示"""def__init__(self,value):self.length=self._count_code_points(value)self.data=valuedef_count_code_points(self,s):"""计算 Unicode 码点数量"""count=0forchins:count+=1returncountdef__len__(self):returnself.length# 实际在 CPython 中的实现""" typedef struct { PyObject_HEAD Py_ssize_t length; // 字符串长度(码点数量) Py_hash_t hash; // 哈希值 struct { unsigned int interned:2; unsigned int kind:3; // 存储类型(1字节/2字节/4字节) unsigned int compact:1; unsigned int ascii:1; unsigned int ready:1; } state; wchar_t *wstr; // 宽字符指针 } PyUnicodeObject; Py_ssize_t PyUnicode_GetLength(PyObject *unicode) { return ((PyUnicodeObject*)unicode)->length; } """# Python 3 统计的是 Unicode 码点,不是字节s1="a"# 1个字符,1个码点s2="好"# 1个字符,1个码点s3="🎉"# 1个字符,1个码点(但占用2个UTF-16代码单元)s4="🇨🇳"# 1个字符(但由多个码点组成)s5="café"# 4个字符,"é"是一个码点print(len(s1),len(s2),len(s3),len(s5))# 1 1 1 4# 注意:组合字符的情况s6="caf\u0065\u0301"# "cafe" + 重音符号print(s6)# "café"(显示为一个字符)print(len(s6))# 5(实际上是5个码点)print(list(s6))# ['c', 'a', 'f', 'e', '\u0301']# 统计字符串中的行数text="line1\nline2\nline3"lines=text.splitlines()# ['line1', 'line2', 'line3']line_count=len(lines)# 3# 底层:splitlines() 识别多种换行符print("A\nB\r\nC\rD".splitlines())# ['A', 'B', 'C', 'D']# 方法1:使用 readlines()withopen('file.txt','r')asf:lines=f.readlines()# 读取所有行到列表line_count=len(lines)# 调用列表的 __len__()# 底层:readlines() 内部实现(简化版)classTextIOWrapper:defreadlines(self,hint=-1):lines=[]whileTrue:line=self.readline()ifnotline:breaklines.append(line)returnlines# 方法2:逐行计数(内存友好)defcount_lines(filename):count=0withopen(filename,'r',buffering=1024*1024)asf:# 1MB缓冲# 使用迭代器,不存储所有行for_inf:count+=1returncount# 方法3:使用缓冲区(最快的方法之一)deffast_line_count(filename):"""最快的行数统计方法之一"""count=0buffer_size=1024*1024# 1MBwithopen(filename,'rb')asf:# 二进制模式更快buffer=f.read(buffer_size)whilebuffer:count+=buffer.count(b'\n')# 统计换行符buffer=f.read(buffer_size)returncount# 测试性能importtimeit filename='large_file.txt'print("方法1时间:",timeit.timeit(lambda:len(open(filename).readlines()),number=10))print("方法2时间:",timeit.timeit(lambda:sum(1for_inopen(filename)),number=10))print("方法3时间:",timeit.timeit(lambda:fast_line_count(filename),number=10))__len__()实现# 1. 列表:记录元素个数classList:def__init__(self):self.ob_item=[]# 元素数组self.allocated=0# 已分配空间self.size=0# 实际元素数量defappend(self,item):# 添加元素逻辑...self.size+=1def__len__(self):returnself.size# 直接返回计数器# 2. 字典:存储键值对数量classDict:def__init__(self):self.ma_used=0# 已使用的条目数# ... 其他字典结构def__len__(self):returnself.ma_used# 3. 集合:类似字典classSet:def__len__(self):returnself.used_count/* CPython 中 len() 的实际实现 (Objects/abstract.c) */staticPyObject*builtin_len(PyObject*module,PyObject*obj){Py_ssize_t res;res=PyObject_Size(obj);// 获取对象大小if(res<0){if(PyErr_Occurred()){returnNULL;// 出错}/* 如果对象没有 __len__,抛出 TypeError */PyErr_SetString(PyExc_TypeError,"object of type '%.200s' has no len()",Py_TYPE(obj)->tp_name);returnNULL;}returnPyLong_FromSsize_t(res);// 转换为 Python 整数}/* PyObject_Size 的实现 */Py_ssize_tPyObject_Size(PyObject*o){PySequenceMethods*m;if(o==NULL){return-1;}m=Py_TYPE(o)->tp_as_sequence;if(m&&m->sq_length){returnm->sq_length(o);// 调用序列的 sq_length}// 尝试调用对象的 __len__ 方法returnPyObject_Length(o);}__len__()实现classTextDocument:"""自定义文本文档类"""def__init__(self,content):self.content=content self._line_count=Noneself._char_count=Nonedef_count_chars(self):"""统计字符数"""count=0forcharinself.content:count+=1self._char_count=countreturncountdef_count_lines(self):"""统计行数"""ifnotself.content:self._line_count=0else:# 统计换行符,考虑最后一行可能没有换行符self._line_count=self.content.count('\n')ifnotself.content.endswith('\n'):self._line_count+=1returnself._line_countdef__len__(self):"""返回字符数(类似字符串)"""ifself._char_countisNone:self._count_chars()returnself._char_countdefline_count(self):"""返回行数"""ifself._line_countisNone:self._count_lines()returnself._line_countdefword_count(self):"""统计单词数"""importre words=re.findall(r'\b\w+\b',self.content)returnlen(words)# 使用示例doc=TextDocument("Hello world!\nThis is a test.\nPython is awesome.")print(f"字符数:{len(doc)}")# 调用 __len__(),返回 55print(f"行数:{doc.line_count()}")# 3print(f"单词数:{doc.word_count()}")# 9importsys# 1. 不同字符串长度的内存占用short="a"long_str="a"*1000print(f"短字符串长度:{len(short)}")# 1print(f"长字符串长度:{len(long_str)}")# 1000print(f"短字符串内存:{sys.getsizeof(short)}字节")# ~50字节print(f"长字符串内存:{sys.getsizeof(long_str)}字节")# ~1049字节# 2. 大数据量的性能考虑classLazyTextAnalyzer:"""惰性计算的文本分析器"""def__init__(self,filename):self.filename=filename self._line_count=Noneself._char_count=None@propertydefline_count(self):ifself._line_countisNone:# 惰性计算self._line_count=self._calculate_line_count()returnself._line_countdef_calculate_line_count(self):count=0withopen(self.filename,'r')asf:for_inf:count+=1returncount@propertydefchar_count(self):ifself._char_countisNone:self._char_count=self._calculate_char_count()returnself._char_countdef_calculate_char_count(self):total=0withopen(self.filename,'r')asf:forlineinf:total+=len(line)returntotal# 使用惰性计算analyzer=LazyTextAnalyzer('large_file.txt')print(f"行数:{analyzer.line_count}")# 第一次调用时才计算print(f"字符数:{analyzer.char_count}")# 第一次调用时才计算len()的底层原理:
__len__()方法文本行数的真相:
len(文件)的方法readlines())或迭代计数字符串字数的真相:
len(s.encode('utf-8'))