拓冰建站拓冰建站
首页 / 资讯中心 / 正文

提升Python效率:这10个内置函数你用过几个?

你还在用for i in range(len(lst))遍历列表吗你还在靠if x in list重复检查成员关系吗Python 最迷人的地方正是那些就藏在标准库里的内置函数——它们不是炫技的道具而是能把代码从“能跑”提升到“优雅且高效”的利器。我见过太多人写了三年 Python却对enumerate、zip视而不见宁愿自己维护计数器也不肯多看一眼文档。今天我们不谈框架、不谈算法优化就聚焦这 10 个内置函数用具体场景告诉你效率不是微秒级的抠搜而是用对了工具让代码从骨子里透出干净和聪明。enumerate别再用range(len())自讨苦吃拿到一个列表同时需要下标和元素最常见的原始写法是for i in range(len(items)): print(i, items[i])这行代码犯了三个错误第一range(len())让读代码的人要先反应一下“这个 i 是下标吗”第二访问items[i]是在做一次下标运算虽然快但没必要第三一旦items是生成器或迭代器len()直接崩溃。而enumerate一行解决for i, item in enumerate(items): print(i, item)enumerate是 Python 给你的免费计数器它让你的循环同时拥有位置和内容并且对任何可迭代对象一视同仁。如果你还习惯写for i in range(...)请问问自己我真的需要一个随机访问的序列吗如果是你拿到的可能并不是迭代器。另外别忘了enumerate(list, start1)这个参数多数人写i1的写法是在让自己的代码显得多余。清理掉所有range(len())的代码你的代码可读性会立刻上升一个台阶。zip并行遍历别再表演“多变量下标舞蹈”当你需要同时遍历两个或多个列表时常见的低效又丑陋的写法是这样的for i in range(len(names)): print(names[i], ages[i])zip把这些并行序列“拉链”成一个元组序列让你直接解包for name, age in zip(names, ages): print(name, age)更妙的是zip是惰性的它返回一个迭代器不把所有元组都生成。如果两个列表长度不一zip默认以短者为准。如果你是想以长者为准请用itertools.zip_longest但我更想问你的数据凭什么长短不一使用zip还能轻松构造字典dict(zip(keys, values))这比手动循环赋值快且清晰得多。并行遍历的正确姿势只有一个zip。任何还在用range(len())同时访问多个列表的人都在重复造轮子而且造的是方轮子。map把循环里的“算了再存”变成流式处理假设你有一个价格列表需要计算所有含税价格。很多人写成这样taxed [] for p in prices: taxed.append(p 1.2)这只用了三行但本质上你在手动维护一个taxed列表然后不断append。map将一个函数应用到每个元素并返回一个迭代器taxed map(lambda p: p 1.2, prices)如果你想得到列表再套一层list()即可。有人会说“列表推导式更 Pythonic”但map有自己独特的优势当转换函数已经存在时map能让代码短到极致并且完全避免显式循环的副作用。比如map(str, data)、map(len, words)一行完成。并且map是惰性的在处理超长序列时不会立刻占用大量内存。能用map的时候不要写列表推导式因为推导式会立即生成整个列表而map可以随时中断像一个按需供水的管道。filter筛掉垃圾数据别再用if堆出金字塔数据清洗中最常见的操作是过滤。原始写法valid [] for item in items: if item 0: valid.append(item)而filter把“判断”和“收集”分开让你直击要害valid filter(lambda x: x 0, items)filter同样惰性并且和map可以完美组合。想想这段代码positive_squares map(lambda x: x2, filter(lambda x: x 0, nums))一行内完成了“筛正数再求平方”的流水线而传统写法需要至少五行的循环加条件加临时列表。filter不是用来替代列表推导式的它是用来让你的数据流像过滤器一样直白——能一眼看出“先筛什么后转什么”。当然如果条件本身复杂推导式的可读性可能更好但对于简单条件filter和map的组合拳是函数式编程在 Python 里最优雅的体现。any和all终结“标志位”噩梦“判断列表里是否存在奇数”怎么写新手has_odd False for n in nums: if n % 2 ! 0: has_odd True break这五行的核心思想是“先假设为假遇到真就跳出”。Python 给你any()has_odd any(n % 2 ! 0 for n in nums)同样“所有元素都大于0”用all()all_positive all(n 0 for n in nums)any和all自带短路特性any找到第一个真值立即返回 Trueall找到第一个假值立即返回 False。这比手动写循环还快因为循环里的break需要你多两行代码。更重要的是any和all把“是否存在”和“是否全部”变成了一个可读的表达式而不是一段控制流。如果你发现自己代码中出现了flag True然后循环里if flag: ...请立刻用any/all重写。它们同样适合校验函数的返回值比如any(result expected for result in results)。sorted的key参数排序的“灵魂”sorted人人都懂但多数人只知道sorted(list)。真正让sorted脱胎换骨的是key参数——它定义了“按什么排序”。比如按字符串长度排序sorted(words, keylen)按元组第二个元素排序sorted(pairs, keylambda p: p[1])更进阶的是按属性排序一个类Person有name和age按年龄排序sorted(people, keylambda p: p.age)key参数会为每个元素调用一次而不是在比较时反复调用这保证了排序的高效。如果你用cmp参数Python 2 旧物每次比较都要重新计算效率天差地别。记住永远不要写sorted(items, keylambda x: (x.age, x.name))来复合排序因为元组比较会先看第一个元素相同再看第二个——这正是“先按年龄排年龄相同按名字排”的最简表达。如果你需要对字典按键或值排序sorted(d.items(), keylambda i: i[1])也会让字典的“无序”变得有序可控。max和min别只会数字比大小max和min同样支持key参数这意味着你能轻松找出“最长字符串”“最大年龄的人”longest max(words, keylen) oldest_person max(people, keylambda p: p.age)这样一段代码比先排序取最后一个或者手动遍历比较要快得多。因为max/min的时间复杂度是 O(n)而排序是 O(n log n)。用max就不要先排序那是在拿大炮打蚊子还丢了性能。还有一个被忽略的default参数当序列为空时max([], default0)返回 0而不会抛ValueError。这让你避免写一堆if not list:的防御代码。让max和min承担“找最优”的责任你的循环里就少一个变量少一次比较。getattr动态调用的“万金油”终结 if-else 地狱当你想根据一个字符串名字来调用对象的方法或属性很多人会写if method_name foo: obj.foo() elif method_name bar: obj.bar()如果方法有十几个这就是一堵墙。getattr让你直接用字符串去取属性getattr(obj, method_name)()getattr把“方法名字符串”和“函数调用”之间的桥梁缩写成一个函数让代码量缩水十倍且可扩展性极强。它还提供默认值参数getattr(obj, nope, 42)返回 42替代了if hasattr(obj, nope): ... else: ...。用getattr改写那些多分支的分发器你会发现原来优雅的动态分发只需要一行。比如写一个简单的命令行工具根据参数名调用对应函数getattr(funcs, command)就足够了。当然要小心使用但大部分担心都是多余的——真正的危险不是getattr而是你的字符串没有经过校验。next迭代器的“遥控器”next是什么它是从迭代器中取下一项的内置函数。很多人在处理文件、生成器时只会用for却忘了自己可以手动控制迭代。比如跳过文件的表头with open(data.csv) as f: header next(f) for line in f: ...这里next(f)把第一行消费掉然后循环从第二行开始。next让你在迭代过程中拥有“步进”的能力而for只能从头到尾一股脑跑完。它还是处理生成器弹性的关键gen (x2 for x in range(10)) first next(gen, None)第二个参数是默认值避免StopIteration异常。next配合iter可以手动构建迭代协议也可以用next(it, sentinel)实现“读到某个值停止”的模式。比如next(x for x in nums if x 100, None)快速找到第一个满足条件的元素而不是遍历完所有。divmod一个函数搞定商和余数分页代码终于干净了分页逻辑里你要算总页数通常写pages total // per_page if total % per_page ! 0: pages 1其实内置函数divmod一次性返回商和余数pages, remainder divmod(total, per_page) if remainder: pages 1甚至更优雅地pages total // per_page (1 if total % per_page else 0)但divmod的好处是同时得到两个数如果后面你还想用余数判断是否整除就不需要再算一次取模。divmod让“商和余数”作为一对结果同时出现避免了两次算术运算也避免了变量冗杂。它还能用于进制转换、时间换算小时和分钟、坐标换算等场景。如果你只在需要求商的时候用//需要余数的时候用%但你同时需要两者时请想起divmod。一行代码减少一次重复计算也减少一个潜在的不一致——比如你改了被除数却忘了改模数。这些内置函数不是孤立的它们组合起来可以写出令人惊叹的简洁代码。比如从一串字典中提取所有值大于 10 的键并按值降序排列result sorted( (k for k, v in data.items() if v 10), keylambda k: data[k], reverseTrue )这里有生成器表达式、sorted的key但没有一个临时列表。Python 的效率从来不是靠“写得更快”而是靠“想得更清楚”。当你发现自己手写计数器、手写标志位、手写拼接时先问问自己是不是有个内置函数在做这件事大多数人不用内置函数不是因为不知道而是因为习惯了在低效的舒适区里打转。从今天起把你代码里的range(len(...))换成enumerate把for循环里的if收集换成filter把flag False换成any()。这些改变不用任何第三方库只需要你对 Python 的标准库多一点信任。你会发现代码变短了错误变少了运行的效率也悄然提升——因为当你使用正确的内置函数时解释器已经为你优化好了最底层的循环。效率的终极秘密不是写更少的代码而是做更少的事。内置函数让你把重复的控制流交给 C 语言实现而你的 Python 代码只是拼装逻辑。现在回到你的编辑器用这 10 个函数去重新审视你的旧代码你大概率会拍着脑袋说原来我早该这么写。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门