网站建设动态部分实训报告,做logo网站,平面设计类的网站,杭州网站建设案例len() 函数的底层原理并不是简单计数#xff0c;而是通过不同对象的 __len__() 方法来实现的。
对于不同的数据类型#xff0c;底层实现机制完全不同。
1. len() 的通用原理
# len() 实际调用的是对象的 __len__() 方法
s hello
print(len(s)) # 等价于 s.__le…len()函数的底层原理并不是简单计数而是通过不同对象的__len__()方法来实现的。对于不同的数据类型底层实现机制完全不同。1.len()的通用原理# len() 实际调用的是对象的 __len__() 方法shelloprint(len(s))# 等价于 s.__len__()lst[1,2,3]print(len(lst))# 等价于 lst.__len__()# 验证print(len(hello)hello.__len__())# True2.字符串字数的底层原理# Python 3 字符串Unicode 码点序列textHello 你好 print(len(text))# 10# 底层实现概念性classPyUnicodeObject:Python 字符串对象的简化表示def__init__(self,value):self.lengthself._count_code_points(value)self.datavaluedef_count_code_points(self,s):计算 Unicode 码点数量count0forchins:count1returncountdef__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 码点不是字节s1a# 1个字符1个码点s2好# 1个字符1个码点s3# 1个字符1个码点但占用2个UTF-16代码单元s4# 1个字符但由多个码点组成s5café# 4个字符é是一个码点print(len(s1),len(s2),len(s3),len(s5))# 1 1 1 4# 注意组合字符的情况s6caf\u0065\u0301# cafe 重音符号print(s6)# café显示为一个字符print(len(s6))# 5实际上是5个码点print(list(s6))# [c, a, f, e, \u0301]3.文本行数的底层原理对于字符串中的行数# 统计字符串中的行数textline1\nline2\nline3linestext.splitlines()# [line1, line2, line3]line_countlen(lines)# 3# 底层splitlines() 识别多种换行符print(A\nB\r\nC\rD.splitlines())# [A, B, C, D]对于文件的行数# 方法1使用 readlines()withopen(file.txt,r)asf:linesf.readlines()# 读取所有行到列表line_countlen(lines)# 调用列表的 __len__()# 底层readlines() 内部实现简化版classTextIOWrapper:defreadlines(self,hint-1):lines[]whileTrue:lineself.readline()ifnotline:breaklines.append(line)returnlines高效统计大文件行数# 方法2逐行计数内存友好defcount_lines(filename):count0withopen(filename,r,buffering1024*1024)asf:# 1MB缓冲# 使用迭代器不存储所有行for_inf:count1returncount# 方法3使用缓冲区最快的方法之一deffast_line_count(filename):最快的行数统计方法之一count0buffer_size1024*1024# 1MBwithopen(filename,rb)asf:# 二进制模式更快bufferf.read(buffer_size)whilebuffer:countbuffer.count(b\n)# 统计换行符bufferf.read(buffer_size)returncount# 测试性能importtimeit filenamelarge_file.txtprint(方法1时间:,timeit.timeit(lambda:len(open(filename).readlines()),number10))print(方法2时间:,timeit.timeit(lambda:sum(1for_inopen(filename)),number10))print(方法3时间:,timeit.timeit(lambda:fast_line_count(filename),number10))4.不同数据类型的__len__()实现# 1. 列表记录元素个数classList:def__init__(self):self.ob_item[]# 元素数组self.allocated0# 已分配空间self.size0# 实际元素数量defappend(self,item):# 添加元素逻辑...self.size1def__len__(self):returnself.size# 直接返回计数器# 2. 字典存储键值对数量classDict:def__init__(self):self.ma_used0# 已使用的条目数# ... 其他字典结构def__len__(self):returnself.ma_used# 3. 集合类似字典classSet:def__len__(self):returnself.used_count5.Python 源码中的实际实现/* CPython 中 len() 的实际实现 (Objects/abstract.c) */staticPyObject*builtin_len(PyObject*module,PyObject*obj){Py_ssize_t res;resPyObject_Size(obj);// 获取对象大小if(res0){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(oNULL){return-1;}mPy_TYPE(o)-tp_as_sequence;if(mm-sq_length){returnm-sq_length(o);// 调用序列的 sq_length}// 尝试调用对象的 __len__ 方法returnPyObject_Length(o);}6.自定义类的__len__()实现classTextDocument:自定义文本文档类def__init__(self,content):self.contentcontent self._line_countNoneself._char_countNonedef_count_chars(self):统计字符数count0forcharinself.content:count1self._char_countcountreturncountdef_count_lines(self):统计行数ifnotself.content:self._line_count0else:# 统计换行符考虑最后一行可能没有换行符self._line_countself.content.count(\n)ifnotself.content.endswith(\n):self._line_count1returnself._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 wordsre.findall(r\b\w\b,self.content)returnlen(words)# 使用示例docTextDocument(Hello world!\nThis is a test.\nPython is awesome.)print(f字符数:{len(doc)})# 调用 __len__()返回 55print(f行数:{doc.line_count()})# 3print(f单词数:{doc.word_count()})# 97.性能对比和注意事项importsys# 1. 不同字符串长度的内存占用shortalong_stra*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.filenamefilename self._line_countNoneself._char_countNonepropertydefline_count(self):ifself._line_countisNone:# 惰性计算self._line_countself._calculate_line_count()returnself._line_countdef_calculate_line_count(self):count0withopen(self.filename,r)asf:for_inf:count1returncountpropertydefchar_count(self):ifself._char_countisNone:self._char_countself._calculate_char_count()returnself._char_countdef_calculate_char_count(self):total0withopen(self.filename,r)asf:forlineinf:totallen(line)returntotal# 使用惰性计算analyzerLazyTextAnalyzer(large_file.txt)print(f行数:{analyzer.line_count})# 第一次调用时才计算print(f字符数:{analyzer.char_count})# 第一次调用时才计算敲黑板十一剑的CS_DN博客len()的底层原理通用机制调用对象的__len__()方法时间复杂度通常是 O(1)因为长度被缓存数据类型差异字符串统计 Unicode 码点数量列表/元组返回元素个数字典/集合返回键值对数量文件行数实际是列表长度或计数循环文本行数的真相并没有直接len(文件)的方法需要先将文件内容转换为列表如readlines()或迭代计数对于大文件推荐使用迭代器方式避免内存问题字符串字数的真相统计的是Unicode 码点不是字节对于组合字符、表情符号等特殊字符需要特别注意如果需要字节数使用len(s.encode(utf-8))