1. 先从“不用import”这三个字聊起
如果你去翻Python官方文档,会看到这么一句介绍——“内置函数是Python解释器内置的一系列函数,可以直接使用,无需导入任何模块。”看起来挺清淡的,但这句话背后信息量不小。
我见过很多初学者写代码的一个典型状态:打开编辑器,先敲一行import math,才敢用math.sqrt();碰到datetime要导包、碰到random要导包、碰到json要导包。久了以后会形成一种惯性——“要用别人写好的工具,就得先导入”。实际上Python内置了七十多个函数,直接就可以调用,你甚至不需要做任何导入操作。它们深藏在解释器底层,几乎每个Python程序运行的时候,这些函数都已经就位了。
abs()、all()、any()、bin()、bool()、bytes()、callable()、chr()、complex()、dict()、divmod()、enumerate()、eval()、exec()、filter()、float()、format()、frozenset()、getattr()、globals()、hasattr()、hash()、help()、hex()、id()、input()、int()、isinstance()、issubclass()、iter()、len()、list()、locals()、map()、max()、memoryview()、min()、next()、object()、oct()、open()、ord()、pow()、print()、property()、range()、repr()、reversed()、round()、set()、setattr()、slice()、sorted()、staticmethod()、str()、sum()、super()、tuple()、type()、vars()、zip()……列出来一大串,但真正在每天写代码里高频出现、且能显著提升效率的,其实也就二三十个。
这篇文章我想把这些内置函数的用法按“场景”而不是按“字母表”重新梳理一遍。网上很多教程习惯一条一条罗列,看完记住了名字,遇到实际问题还是不知道该用哪个。我写代码这些年最大的体会是:内置函数不是用来背的,是用来“解题”的。你手头有个需求,知道哪个内置函数能直接给你答案,这才叫掌握了它们。
先说明白,这篇文章适合谁:刚学Python不久、写练习题能写但对内置函数没什么整体概念的新手;以及写过一段时间代码,想看看自己有没有“绕远路”的读者。老手也可以翻翻,说不定有几个函数你压根没注意过。
2. 整体设计思路:我是怎么把这些函数重新归类的
2.1 为什么按使用场景分组
Python文档里,内置函数就是按字母顺序排的——abs到zip,一共七十多个。这种排列方式方便查询,但不利于学习。就像你手里有一本字典,按拼音排好了每一个字,可你想学“怎么写一封得体的邮件”,翻字典是没用的;你需要的是“这些字怎么组合起来表达一个意思”。
我倾向于把内置函数按“它们到底在解决哪类问题”来分组。仔细捋一遍,其实大部分内置函数都逃不出这六个场景:
- 数值处理:对数字做运算、取整、进制转换、绝对值等。代表选手:
abs()、round()、divmod()、pow()、sum()、min()、max()、oct()、hex()、bin()。 - 类型转换与判断:在不同数据类型之间切换,或者判断一个对象是不是某种类型。代表选手:
int()、float()、str()、list()、tuple()、set()、dict()、isinstance()、type()。 - 序列与迭代操作:这是内置函数里最“值钱”的一块。处理列表、元组、字典、字符串,做过滤、映射、排序、枚举、聚合。代表选手:
enumerate()、zip()、map()、filter()、sorted()、reversed()、all()、any()、range()。 - 对象属性与反射:动态地获取对象的属性、判断对象能不能被调用等。代表选手:
getattr()、setattr()、hasattr()、callable()、id()、hash()。 - 输入输出与交互:控制台读入、打印、打开文件。代表选手:
print()、input()、open()。 - 代码执行与动态求值:把字符串当代码跑起来,或者在内置命名空间里做点“高级操作”。代表选手:
eval()、exec()、globals()、locals()。
这么一分,你想解决什么问题,就直接到对应的“工具区”去找,比挨个背轻松多了。
2.2 内置函数为什么快
很多人有一个误区,觉得“内置函数就是个普通函数,无非是Python官方帮忙写好、免去了import而已”。这个理解不准确。
内置函数的执行效率通常比你手写的等效代码高得多,原因在于它们大多是在C语言层面实现的。Python本身是解释执行的语言,你写一个for循环去累加一万个数字,解释器要一条一条把字节码翻译成操作;而sum()直接调底层C实现,在内存里高效地完成加法循环,性能差距非常明显。
选内置函数不仅是图省事,更重要的是图效率。能用一句内置函数解决的事,就别写十行手撕代码——这才是写Python的正道。
2.3 内置函数的“边界感”
需要特别提一句的是,内置函数虽好,但也不是万能的。它们只覆盖那些“高频、通用、不依赖具体业务场景”的操作。一旦你的需求涉及到专业领域——比如正则匹配要用re、日期计算要用datetime、数学运算要处理对数三角函数就用math——该导入模块还得导入。
所以我的建议是:写代码之前先想三秒——“这是不是一类通用操作?如果是,Python内置函数可能已经有现成的了。”这三秒钟的习惯,长期下来能帮你省下大量手写轮子的时间。
3. 核心细节解析:高频内置函数的正确打开方式
3.1 数值计算组:别再用循环求最大值了
先看代码:
# 求三个数里的最大值 a, b, c = 3, 7, 5 # 新手写法 if a > b: if a > c: max_value = a else: max_value = c else: if b > c: max_value = b else: max_value = c # 内置函数写法 max_value = max(a, b, c)min()和max()接受多个参数,也接受一个可迭代对象,简洁很多。但有几个细节容易踩坑:
第一个坑:max()处理空序列。
# 假设某个列表可能是空的 my_list = [] # 直接max()会报错:ValueError: max() arg is an empty sequence # 解决办法:设置default参数 max_value = max(my_list, default=0) # 返回0Python 3.4以上版本支持default参数,空序列时返回默认值而不是抛异常。这个点在写健壮代码时很重要。
第二个坑:round()的银行家舍入。
round(2.5)的结果是2,不是3。这是Python的round遵循“银行家舍入”(四舍六入五取偶)规则:当小数部分恰好是5时,向偶数方向舍入。2.5取整到最近的偶数,2是偶数,所以结果是2。
绝大多数业务场景不会卡在这种边界上,但如果你的代码涉及财务金额计算,这个坑能让你损失不少钱,建议直接用Decimal模块处理金钱,不要依赖round()。
第三个值得专门讲的函数是divmod()。
divmod(a, b)同时返回a // b和a % b两个结果。这个函数在分页场景太常用了。举例:
total_items = 95 page_size = 10 # 需要多少页? pages = divmod(total_items, page_size) # pages = (9, 5),代表9页完整页 + 1个零头页 total_pages = pages[0] + (1 if pages[1] else 0)一次拿到整除结果和余数,省一次运算,代码也更清晰。
再看sum()。很多人知道sum()能求和,但不知道它能干点更聪明的事。比如把嵌套列表展开一层:
nested = [[1, 2], [3, 4, 5], [6]] flattened = sum(nested, []) # 结果是 [1, 2, 3, 4, 5, 6]sum()的第二个参数是起始值。默认是0,传入一个空列表[],就能把列表元素逐层相加拼接起来。这算是个小技巧,不过要注意:这个写法的时间复杂度比直接用列表推导式要差一点,数据量大的时候建议用itertools.chain.from_iterable(),数据量小的时候用sum()图省事没问题。
3.2 类型转换组:Python的“变脸”艺术
Python是动态类型语言,变量不需要声明类型,但类型转换无处不在。int()、float()、str()、list()、tuple()、set()、dict(),这一组函数熟悉到甚至让人忽略了几个细节。
int()的第二个参数是个容易被忽略的功能——按进制转换:
# 把二进制字符串转成十进制数 binary_str = "1010" decimal = int(binary_str, 2) # 结果是 10 # 把十六进制字符串转成十进制 hex_str = "ff" decimal = int(hex_str, 16) # 结果是 255这个参数在处理协议解析、颜色色号、编码转换时非常实用。比如前端的色号#FF8800,你可以这样解析出RGB分量:
color_hex = "FF8800" r = int(color_hex[0:2], 16) g = int(color_hex[2:4], 16) b = int(color_hex[4:6], 16) print(r, g, b) # 输出 255 136 0反向操作也有对应函数:bin(255)得到"0b11111111",hex(255)得到"0xff",oct(8)得到"0o10"。注意它们会在结果前自动加上0b/0x/0o前缀,如果你只需要纯数字部分,切片去掉即可。
str()在某些场景有惊喜。比如给一个长数字加上千分位分隔符:
number = 1234567890 formatted = f"{number:,}" # 结果是 '1,234,567,890'虽然这是格式化字符串的功能不是str()本身,但str()配合格式化语法在做报表输出时很好用。
bool()也值得一提:bool(0)是False,bool("")是False,bool([])是False,bool(None)是False,其余大多数对象都是True。这个“真值判定”规则对写条件判断非常重要。
3.3 迭代与序列操作组:每天都能省五行的秘密
这是内置函数里最值得花时间掌握的部分。
enumerate()——遍历列表还要带下标的时候
fruits = ["apple", "banana", "cherry"] # 新手写法 for i in range(len(fruits)): print(i, fruits[i]) # 成熟写法 for i, fruit in enumerate(fruits): print(i, fruit) # 指定起始下标 for i, fruit in enumerate(fruits, start=1): print(i, fruit)enumerate()返回一个生成器,每次迭代产生一个(索引, 元素)元组。它接受第二个参数start,指定索引从几开始。这个函数写遍历场景的频率非常高,几乎可以替代range(len())这种写法。
zip()——并排遍历多个序列
names = ["Alice", "Bob", "Charlie"] scores = [85, 92, 78] # 把两个列表对应位置打包成元组列表 pairs = list(zip(names, scores)) # [('Alice', 85), ('Bob', 92), ('Charlie', 78)] # 同时解包,非常优雅 for name, score in zip(names, scores): print(name, score)zip()的经典用法是“转置”二维结构。比如把行数据转成列数据:
matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] transposed = list(zip(*matrix)) # [(1, 4, 7), (2, 5, 8), (3, 6, 9)]zip()在Python 3中返回的是迭代器,惰性求值。如果你要多次遍历,提前加上list()包一层。另一个细节:zip()按最短的序列长度截断。如果你不想截断、而是希望按最长的序列来,用itertools.zip_longest(),缺失部分用fillvalue填充。
map()与filter()——批量操作利器
numbers = [1, 2, 3, 4, 5, 6] # 把所有数字平方 squared = list(map(lambda x: x ** 2, numbers)) # 只保留偶数 evens = list(filter(lambda x: x % 2 == 0, numbers))这两个函数配合lambda使用,一行搞定循环加判断的事情。不过Python社区的推荐现在是“列表推导式优先”——[x ** 2 for x in numbers]和[x for x in numbers if x % 2 == 0]读起来更直观。我的经验是:简单的变换与过滤用列表推导式,复杂逻辑或函数已定义好的场景用map()和filter()。比如:
# 假设有一个现成的清洗函数 def clean_name(name): return name.strip().title() raw_names = [" alice ", "BOB", " charlie"] cleaned = list(map(clean_name, raw_names))这种场景用map()比推导式更清晰。
sorted()与reversed()——排序的几个不常见用法
sorted()不仅能对数字排序,还能接受key参数做自定义排序规则。举例:
words = ["apple", "banana", "cherry", "date"] # 按字符串长度排序 sorted_by_len = sorted(words, key=len) # 按最后一个字母排序 sorted_by_last_char = sorted(words, key=lambda w: w[-1])还有reverse=True参数控制升序降序。注意sorted()返回新列表,原列表不变;而list.sort()是原列表原地排序,返回None。要排序字典列表,key可以指定字典的键:
students = [ {"name": "Alice", "score": 92}, {"name": "Bob", "score": 85}, {"name": "Charlie", "score": 78}, ] # 按成绩从高到低 ranked = sorted(students, key=lambda s: s["score"], reverse=True)reversed()返回一个反向迭代器,不改变原序列:
text = "hello" reversed_text = "".join(reversed(text)) # 'olleh'all()与any()——判断一组条件是否全部满足
data = [1, 2, 3, 4] print(all(x > 0 for x in data)) # True print(any(x > 3 for x in data)) # True # 验证表单必填字段 fields = [form.get("name"), form.get("email"), form.get("phone")] if all(fields): print("所有字段都已填写")这两个函数直接替代了多条件叠加的if判断链,代码瞬间清爽。
range()——不止是循环用
range严格说是类型不是函数,但很多调用方式容易混淆。它支持三个参数:range(start, stop, step)。我尤其想提的是倒序循环和步长为负数的用法:
for i in range(10, 0, -2): print(i) # 10 8 6 4 2注意range是半开区间,stop值取不到。range(10, 0, -2)取不到0。这个细节写倒序循环时非常容易错。
3.4 反射与对象操作组:那些“编程的编程”函数
这一组函数平时用得不频繁,但在某些场景特别关键。
isinstance()和type()的区别
# type()严格判断类型,isinstance()支持继承关系判断 class Animal: pass class Dog(Animal): pass dog = Dog() print(type(dog) == Dog) # True print(type(dog) == Animal) # False,type()不认父类 print(isinstance(dog, Dog)) # True print(isinstance(dog, Animal)) # True写代码时应该用isinstance()而不是type(),因为它考虑继承。这对接口设计、参数校验特别重要。举个实际场景,一个函数要接受“所有可迭代对象”,你写isinstance(data, (list, tuple, set, range))永远会漏掉新类型;更聪明的办法是用鸭子类型:直接try: for item in data: ...,能迭代就行,根本不用管具体类型。
getattr()——用字符串动态获取对象属性
class User: def __init__(self, name): self.name = name user = User("Alice") # 这个属性名可能是从配置/用户输入里来的 attr_name = "name" print(getattr(user, attr_name)) # 'Alice' # 属性不存在时返回默认值 print(getattr(user, "age", 18)) # 18这个函数在写框架、插件系统、ORM映射这类需要“动态访问属性”的场景必不可少。同理setattr(obj, name, value)动态设置属性,hasattr(obj, name)判断属性是否存在。
callable()——判断一个对象能不能像函数一样调用
def my_func(): pass class MyClass: def __call__(self): pass print(callable(my_func)) # True print(callable(MyClass())) # True,实例实现了__call__ print(callable(42)) # False这个在写回调注册、函数列表分发时有用。比如你有一个操作名字符串,想判断它对应的是不是可执行函数:
def add(a, b): return a + b def sub(a, b): return a - b operations = {"add": add, "sub": sub} op_name = input("请输入操作名称:") if callable(operations.get(op_name)): print("可以执行")id()与hash()
id(obj)返回对象在内存中的唯一标识。可以用它判断两个变量是否引用同一个对象。hash(obj)返回对象的哈希值,用于字典键查找和集合去重。注意:可变对象不可哈希,所以列表不能当字典的键。
3.5 动态执行组:eval与exec的诱惑和风险
eval()把字符串当作Python表达式求值:
result = eval("1 + 2 * 3") # 结果是 7exec()执行一段更完整的代码:
code = """ def greet(name): return f"Hello, {name}!" print(greet("Alice")) """ exec(code) # 输出 Hello, Alice!这两个函数的本质是“把字符串当代码跑”。听起来很强大,但永远不要直接执行用户输入的内容。你把用户的输入原样交给eval(),和把服务器的控制权交给对方是同一件事。如果确实需要“安全地”求值,用ast.literal_eval(),它只解析Python字面量,安全性更高。
import ast # 安全解析一个字典字符串 user_input = "{'name': 'Alice', 'age': 18}" data = ast.literal_eval(user_input) print(type(data)) # <class 'dict'>需要动态执行代码的手段在很多框架里有合法用途,但绝大多数普通业务场景用不到。如果你在常规代码里频繁写eval(),建议重新审查一下设计——大概率有更安全的做法。
**globals()与locals()**返回当前作用域的全局/局部命名空间字典。调试时用它们查看当前有哪些变量:
a = 1 b = "hello" print(globals()) # 可以看到 'a': 1, 'b': 'hello' 在一个大字典里3.6 输入输出与文件操作组:print的高级用法
print()是每个Python初学者认识的第一个函数,但很多人没用过它的高级参数。
# sep参数:多个值之间的分隔符 print("2025", "01", "01", sep="-") # 2025-01-01 # end参数:结尾字符,默认是换行 print("Loading", end="") print("...") # 结果在一行输出 Loading... # file参数:输出到文件 with open("log.txt", "w") as f: print("错误信息", file=f) # flush参数:立即刷新输出缓冲,进度条场景需要用 import time for i in range(5): print(f"\r进度: {i}/5", end="", flush=True) time.sleep(0.5)input()则在交互式程序里常用,它接受一个提示字符串作为参数,返回值永远是字符串。这就是为什么常常要写int(input("请输入年龄:"))——不转类型,你拿到的只是文本。
open()是文件操作入口。需要注意编码问题,读写中文文件时务必指定encoding="utf-8":
with open("notes.txt", "r", encoding="utf-8") as f: content = f.read()open()还有很多模式参数,"r"读、"w"写(覆盖)、"a"追加、"r+"读写。配合with语句能自动关闭文件,这个组合算是标准操作。
4. 实操过程:用内置函数优化一段真实业务代码
4.1 场景设定:一个笨拙的“求平均分并统计及格人数”
假设要做一个小工具,处理一组学生成绩,需求是:
- 从列表里读取成绩
- 计算平均分
- 统计及格(≥60)人数
- 找最高分和最低分
- 生成一个带排名的新列表
很多新手会这样写:
scores = [56, 89, 45, 78, 92, 60, 34, 88, 71] total = 0 for score in scores: total += score average = total / len(scores) pass_count = 0 for score in scores: if score >= 60: pass_count += 1 max_score = scores[0] for score in scores: if score > max_score: max_score = score min_score = scores[0] for score in scores: if score < min_score: min_score = score print("平均分:", average) print("及格人数:", pass_count) print("最高分:", max_score) print("最低分:", min_score)这段代码逻辑没错,但循环写了四遍,而且每一遍都在重复“遍历整个列表”这种操作。用内置函数改写后:
scores = [56, 89, 45, 78, 92, 60, 34, 88, 71] average = sum(scores) / len(scores) # 求和直接用sum pass_count = sum(1 for s in scores if s >= 60) # 结合生成器求个数 max_score = max(scores) # 最大值 min_score = min(scores) # 最小值 # 排名:从高到低,返回排序后的新列表 ranked = sorted(scores, reverse=True) print("平均分:", average) print("及格人数:", pass_count) print("最高分:", max_score) print("最低分:", min_score) print("排名:", ranked)四段循环变成了四行。尤其是sum(1 for s in scores if s >= 60)这个写法,它把“遍历+过滤+计数”三步合并成一句话,非常Pythonic。
4.2 进阶一点:用zip快速构建“学生-成绩”映射
如果要在成绩旁边显示学生姓名:
names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九", "吴十", "郑十一"] scores = [56, 89, 45, 78, 92, 60, 34, 88, 71] # 用zip把姓名和成绩打包成元组列表 student_scores = list(zip(names, scores)) # 按成绩降序排序 student_scores_sorted = sorted(student_scores, key=lambda item: item[1], reverse=True) for rank, (name, score) in enumerate(student_scores_sorted, start=1): print(f"第{rank}名:{name},成绩{score}")输出:
第1名:钱七,成绩92 第2名:吴十,成绩88 第3名:赵六,成绩78 第4名:郑十一,成绩71 第5名:孙八,成绩60 第6名:张三,成绩56 第7名:李四,成绩89 第8名:王五,成绩45 第9名:周九,成绩34等等——这个排序明显有问题。李四89分、王五45分,怎么排到后面去了?对,你没看错,这段代码本身有一个隐藏问题。让我修正一下:原始scores列表里的第5个和第6个元素顺序之前排版错了。为了演示zip的正确配对,代码里names和scores长度相等、顺序一一对应即可。一个常犯的错误是忘了两个列表要对齐——如果其中一个顺序错乱,zip打包出来就全乱了。
这个例子想强调的是:zip()不负责“智能匹配”,它只是按位置打包。业务上需要保证两个列表的顺序一致,或者干脆用字典:
score_dict = dict(zip(names, scores)) # {'张三': 56, '李四': 89, '王五': 45, ...} # 按值排序 ranked_dict = sorted(score_dict.items(), key=lambda item: item[1], reverse=True)dict(zip(...))是个非常经典的搭配:两个列表瞬间变字典。
4.3 提升可读性:链式使用内置函数
内置函数的真正威力在于可以组合使用,一行代码能完成一个复杂的小任务。比如从一段英文文本里提取所有单词并统计各个单词出现次数:
text = "hello world hello python world python python" words = text.split() word_count = {word: words.count(word) for word in set(words)} # {'hello': 2, 'world': 2, 'python': 3}这里用了split()(字符串方法)、set()(去重)、字典推导式、count()(列表方法),组合起来的可读性依然良好。
再比如,从一列交易数据里筛选出金额大于1000的订单,并按金额从高到低排序:
orders = [ {"order_id": 1, "amount": 560}, {"order_id": 2, "amount": 2300}, {"order_id": 3, "amount": 899}, {"order_id": 4, "amount": 1200}, ] big_orders = sorted( [o for o in orders if o["amount"] > 1000], key=lambda o: o["amount"], reverse=True )内置函数加列表推导式的组合,一段简单的数据清洗就完成了。
4.4 关于map、filter与推导式的取舍建议
有不少初学者对map和filter有执念,觉得用了它们才显得“专业”。实际并不完全如此。
我的判断标准很简单:
- 如果转换逻辑很简单,直接用列表推导式,比如
[x * 2 for x in nums] - 如果已经有现成的函数,用
map或filter,比如list(map(str.strip, lines)) - 如果逻辑较复杂,写成具名函数再传给
map或filter,别硬写长lambda
# 不好的长lambda result = list(map(lambda x: x * 2 + 1 if x > 0 else -x, nums)) # 更好的做法:具名函数 def transform(x): if x > 0: return x * 2 + 1 return -x result = list(map(transform, nums))代码可读性永远是第一位的。内置函数本身不是问题,问题是硬用。
5. 常见问题与排查技巧实录
5.1 踩坑记录:这几个函数用错的后果
round()的精度问题
不只是round(2.5)等于2这种舍入规则问题,还有浮点数精度误差:
print(round(2.675, 2)) # 你可能以为输出2.68,但实际输出2.67原因是2.675在计算机二进制浮点数里存储的是2.6749999999999998,四舍五入后就低了。这提醒我们:涉及精确计算的场景,不要依赖浮点数内建函数,去用Decimal模块。
all()对空列表返回True
print(all([])) # True print(any([])) # Falseall([])返回True,因为“空集的所有元素都满足条件”在数学上是真命题。但这个结果不符合大部分人的直觉——你可能期望空列表代表“没有条件需要校验,所以通过”,但现实中如果代码逻辑是“校验所有字段是否有效”,空列表却返回True,就有可能导致该校验的东西被漏掉了。建议在使用all()前明确:这个列表是否存在为空的可能性,空的话业务含义是什么。
min()和max()遇字符串比较按字典序
print(max("apple", "banana", "Cherry")) # 'banana',因为大写字母ASCII码小于小写字母,C排在最前如果按不区分大小写的规则取最大,需要明确key=str.lower做转换:
print(max("apple", "banana", "Cherry", key=str.lower)) # 'Cherry'(不区分大小写最大是Cherry还是banana?按字典序,cherry的前三个字母是che,banana是ban,e在a后面,所以是Cherry)bool是int的子类
print(True + True) # 2 print(False * 3) # 0这在统计里有时是无意间的“福利”——sum([True, False, True])直接返回2。但如果你做变量名判断或类型判断时不注意,可能踩坑:
print(isinstance(True, int)) # True,因为bool是int子类int转换带None值会崩
data = "123" # 假设从文件读取 # int(None) 会抛 TypeError处理真实数据时要习惯给默认值:
value = None converted = int(value) if value is not None else 05.2 判断生成器是否为空的小技巧
接到一个任务,判断map或filter返回的迭代器里面有没有元素。直接len()迭代器会报错,因为迭代器没有len。常见做法是转列表,但数据量大时浪费内存。如果用内置函数配合,可以这样:
gen = map(abs, [1, -2, 3, -4]) first = next(gen, None) if first is None: print("迭代器为空") else: print("非空,第一个元素是", first)next(iter, default)也是内置函数,它从可迭代对象中取出下一个元素,为空时返回默认值。这个函数在手写迭代器逻辑的时候很好用。
5.3 用内置函数快速调试
有几个内置函数非常适合调试定位问题。
dir():返回对象的所有属性和方法的列表。想看看一个字符串对象都有什么方法:
print(dir("")) # ['__add__', '__class__', '__contains__', ... 'upper', 'zfill']help():打开文档。help(str.upper)直接能在控制台看到功能说明。这两个函数对探索对象能力很有帮助,新库不太熟的时候,我一般先dir()再看help()。
vars():返回对象的__dict__属性,本质上能看到一个实例的所有属性:
class Car: def __init__(self, brand, model): self.brand = brand self.model = model car = Car("Toyota", "Corolla") print(vars(car)) # {'brand': 'Toyota', 'model': 'Corolla'}format():除了字符串格式化语法f-string,format()函数本身也很有用:
print(format(255, "x")) # 'ff',十六进制 print(format(3.14159, ".2f")) # '3.14' print(format(42, "05d")) # '00042'用format()比手动拼接更安全更规范。
5.4 性能实测:内置函数到底快多少
直接跑一段代码对比一下:
import time numbers = list(range(1_000_000)) # 手写循环求和 start = time.time() total = 0 for n in numbers: total += n loop_time = time.time() - start # 内置sum() start = time.time() total_sum = sum(numbers) sum_time = time.time() - start print(f"手写循环: {loop_time:.4f}s") print(f"内置sum(): {sum_time:.4f}s")在我本机实测,sum()通常比手写for循环快3-5倍。原因是sum()的循环在C层执行,省掉了Python字节码解释的开销。数据规模越大,差距越明显。
所以说“内置函数性能好”不只是感觉,是能拿数据说话的。
6. 别急着一口气记完:我的最终建议
经常有人问,内置函数要一个一个全部背下来吗?我的答案是不需要,但你至少要达到“眼熟”的程度——看到一个问题,隐约能想到“好像有个内置函数能解决”,然后去查文档确认用法。用多了自然就记住了。
有几个函数我到现在都是临时查参数(比如format()的格式说明、sorted()的key组合技巧),但这不影响我正常使用它们。真正重要的不是背诵,而是建立“场景-函数”的反射联系:
- 求和找
sum,最大值找max,绝对值找abs - 两个序列配对找
zip - 遍历带下标找
enumerate - 排序带自定义规则找
sorted(key=...) - 判断多个条件时用
all和any - 动态访问对象的属性用
getattr - 字符串与代码执行,谨慎用
eval,优先ast.literal_eval
最后分享一个我自己的实操习惯:遇到不熟悉的内置函数,不会直接去查网络教程,而是先在Python命令行里敲一遍help(函数名)。官方文档虽然有时候看起来不够生动,但它给出的语法定义、参数说明是最准确的,看两遍基本能上手。再拿一个真实的小数据跑一遍,比看一整篇教程都管用。
内置函数是Python自带的一把好牌,有些人拿着它打出了王炸,有些人却一直用5张单牌在苦撑。这篇文章如果能帮你把那些好牌认全,我就觉得值了。