一、内置函数是什么
前面几篇里,我们已经用过好几个「拿来就用」的东西了:print() 负责输出、input() 负责收数据、len() 负责数长度、type() 负责看类型。它们有一个共同的名字——内置函数。
「内置」两个字的意思很直白:Python 装好就自带,不用导入、不用定义,写出来就能用。就像手机里预装的相机和计算器,开机就在那儿,不用自己装。
1.1 内置函数长什么样
用起来只有一个套路:函数名(要处理的东西)。
print("你好") # 把 "你好" 交给 print,它负责显示出来
len([1, 2, 3]) # 把列表交给 len,它负责数出有几个
abs(-5) # 把 -5 交给 abs,它负责变成正的
可以这样理解:你把东西递给一个工具,工具把处理好的结果还给你。括号里的东西叫「参数」,还回来的东西叫「返回值」。
1.2 它和别处的函数有什么不一样
| 称呼 | 怎么来的 | 要不要先准备 | 例子 |
|---|---|---|---|
| 内置函数 | Python 自带 | 不用,装好就有 | len() max() sorted() |
| 模块里的函数 | 别的文件里写好的 | 要先 import 进来 | math.sqrt()(先 import math) |
| 自己写的函数 | 你自己 def 出来的 | 要先定义 | 第 22 篇《函数》里讲 |
第 5 篇《语法规范》里讲过一个容易混的点:print、input、len 这些 不是关键字,而是内置函数。关键字是语法里的零件(不能拿来当变量名),内置函数是现成的工具(名字可以被你覆盖,但强烈不建议)。
1.3 怎么自己查一个函数
- 看说明书:写
help(函数名),会把官方说明打印出来(英文的)。例:help(abs)。 - 看某类东西都会干什么:写
dir(类型)。例:dir(str)能列出一大串名字,第 10 篇讲过的upper()、isalpha()都在里面。 - 翻官方文档:搜「Python built-in functions」,第一条就是。
int、str、bool 这些类型和 True、False 这类常量。真正算「函数」的有几十个,新手要熟练的就是本篇这三十来个,不用全背。1.4 按用途分成七类
本篇按下面这张表来讲。先看个整体印象,后面每一类单独一节。
| 类别 | 包含哪些函数 | 主要用途 |
|---|---|---|
| ① 数学运算类 | abs max min sum pow round divmod | 算绝对值、比大小、求和、四舍五入 |
| ② 类型转换类 | bool int float str list tuple dict set | 把一种类型变成另一种 |
| ③ 序列操作类 | len sorted reversed enumerate zip | 数个数、排序、配编号、两两配对 |
| ④ 高级操作类 | range map filter reduce | 生成数列、批量处理、筛选 |
| ⑤ 字符与编码类 | chr ord | 字符和编号互相转换 |
| ⑥ 输入输出与文件 | print input open | 显示、读键盘、读写文件 |
| ⑦ 对象信息类 | id type eval | 看身份、看类型、执行字符串表达式 |
课程思政|科技自立自强 · 自带的本事最踏实
内置函数是 Python 装好就自带的能力,不用导入、写出来就能用,像手机里预装的相机和计算器。基础越扎实,往上搭的东西才越稳。今天我们可以先用别人的工具,但真正靠得住的,是自己手里那点硬功夫——把 len、sum 这些用熟,将来换成什么工具都不慌。
二、数学运算类(7 个)
这一类最直观:给你一个数或一串数,帮你算出另一个数。
| 函数 | 干什么 | 例子 | 结果 |
|---|---|---|---|
abs(x) | 求绝对值(去掉负号) | abs(-5) | 5 |
max(...) | 求最大值 | max([1, 3, 5, 2]) | 5 |
min(...) | 求最小值 | min([4, 2, 7, 1]) | 1 |
sum(x) | 把一串数字加起来 | sum([1, 2, 3, 4]) | 10 |
pow(x, y) | x 的 y 次方 | pow(2, 3) | 8 |
round(x) | 四舍五入 | round(2.7) | 3 |
divmod(x, y) | 一次拿到商和余数 | divmod(10, 3) | (3, 1) |
2.1 abs():把负号去掉
print(abs(-5)) # 5
print(abs(3.14)) # 3.14
print(abs(-0.25)) # 0.25
常用在「不管正负,我只看差多少」的场合,比如比较两个数相差多大。
2.2 max() 和 min():两种用法都行
它们既能收一串东西,也能收好几个单独的参数——这是新手最容易迷糊的一点。
# 用法一:括号里放一个「一串东西」
print(max([1, 3, 5, 2])) # 5
print(min((4, 2, 7, 1))) # 1
# 用法二:括号里放好几个单独的值
print(max(10, 20, 5)) # 20
print(min(8, 3, 6)) # 3
# 字符串也能比,按「字典顺序」一个字符一个字符比
print(max("abc", "abd", "abe")) # abe
print(min("xyz", "xy", "xya")) # xy (短的更小)
如果要比的不是数值本身,而是「按什么标准比」,可以加 key=:
words = ["apple", "hi", "banana"]
print(max(words)) # banana (字母顺序最后)
print(max(words, key=len)) # banana (按长度比,最长的是它)
print(min(words, key=len)) # hi (最短的是它)
2.3 sum():一串数字求和
print(sum([1, 2, 3, 4])) # 10
print(sum((5, 6, 7))) # 18
print(sum([1, 2, 3], 10)) # 16 (第二个参数是「起点」,从 10 开始加)
sum() 只能加数字。sum([1, "2"]) 会报 TypeError,因为数字和文字不能相加(第 7 篇《运算符》里讲过的老问题)。另外,较新的
Python 3 版本里,sum() 对小数求和做了精度优化:sum([0.1, 0.2, 0.3]) 会得到干净的 0.6,而手写 0.1 + 0.2 + 0.3 会得到 0.6000000000000001(第 8 篇讲过的浮点精度问题)。2.4 pow():次方,还能顺带取余
print(pow(2, 3)) # 8 2 的 3 次方
print(pow(3, 2)) # 9 3 的 2 次方
print(pow(2, 3, 5)) # 3 2³ = 8,8 除以 5 余 3(第三个参数是「再对谁取余」)
平时算次方直接写 2 ** 3 更方便;pow() 的长处是带上第三个参数时,能一次性算出「次方再取余」,处理很大的数字时很快。
2.5 round():四舍五入(但有坑)
print(round(3.14159, 2)) # 3.14 第二个参数是「保留几位小数」
print(round(3.14159, 3)) # 3.142
print(round(2.7)) # 3 不写位数就取整
print(round(4.49)) # 4
round() 并不总是你想的那个「四舍五入」。round(2.5) 得到的是 2(不是 3),round(2.675, 2) 得到的是 2.67。原因和正确用法见第九节 9.3。2.6 divmod():商和余数一次拿走
print(divmod(10, 3)) # (3, 1) 10 除以 3:商 3,余 1
print(divmod(20, 4)) # (5, 0) 正好除尽,余数是 0
print(divmod(7, 2)) # (3, 1)
它是 // 和 % 两个运算符的合体:divmod(10, 3) 等价于 (10 // 3, 10 % 3)。拿到的是个元组,可以一次拆成两个变量:
q, r = divmod(17, 5)
print(q, r) # 3 2
三、类型转换类(8 个)
这一类干的活只有一件:把一种类型变成另一种。第 6 篇里 int(input(...)) 用的就是它。
| 函数 | 变成什么 | 例子 | 结果 |
|---|---|---|---|
bool(x) | 真假值 | bool(0) | False |
int(x) | 整数 | int('10') | 10 |
float(x) | 小数 | float('3.2') | 3.2 |
str(x) | 字符串 | str(123) | '123' |
list(x) | 列表 | list('abc') | ['a', 'b', 'c'] |
tuple(x) | 元组 | tuple([1, 2, 3]) | (1, 2, 3) |
dict(x) | 字典 | dict(a=1, b=2) | {'a': 1, 'b': 2} |
set(x) | 集合(自动去重) | set([1, 2, 2, 3]) | {1, 2, 3} |
3.1 三个最常用的:int / float / str
print(int(3.14)) # 3 小数变整数是「砍掉小数部分」,不是四舍五入
print(int('10')) # 10 文字里的数字能变过来
print(int(True)) # 1 真假值也能变,True 就是 1
print(float(5)) # 5.0
print(float('3.2')) # 3.2
print(float(False)) # 0.0
print(str(123)) # '123' 变回文字
print(str(True)) # 'True'
int(3.14) 是 3,小数部分直接扔掉,不做四舍五入。想四舍五入要另外用 round(3.14)。3.2 转换的结果要「接住」
转换不会改动原来的变量,它只是算出一个新值还给你。必须用变量接住才有用:
x = "18"
int(x) # 算出一个 18,但没人接,马上就被丢掉了
print(x) # 输出 18(还是字符串,前后都有引号)
y = int(x) # 这样才对:用 y 接住
print(y + 1) # 19
int(x) 单独一行什么也不会发生,写 y = int(x) 才留下结果。3.3 列表、元组、字典、集合之间也能互相转
print(list('abc')) # ['a', 'b', 'c'] 字符串拆成单个字符
print(list((1, 2, 3))) # [1, 2, 3] 元组转列表
print(tuple([1, 2, 3])) # (1, 2, 3) 列表转元组
print(set([1, 2, 2, 3])) # {1, 2, 3} 去重
print(sorted(set([3, 1, 3, 2]))) # [1, 2, 3] 去重之后再排序
# 字典要用「键值对」来建
print(dict(a=1, b=2)) # {'a': 1, 'b': 2}
print(dict([('name', '小满'), ('age', 18)])) # {'name': '小满', 'age': 18}
这几个转换在第 8 篇《基本数据类型》里都埋过伏笔,现在你知道它们其实都是内置函数了。
课程思政|工匠精神与严谨求实 · 转换不是想当然
类型转换不是许愿,int('3.5') 会直接报错——带小数点的文字 int() 认不出来,得先 float() 再 int()。工具只按规则办事,不会帮你猜。写代码也好、做统计也好,先弄清手里这串东西到底是什么、能不能转,比出了错再回头查要省事得多。
四、序列操作类(5 个)
这一类管的是「一串东西」——字符串、列表、元组、字典都算。数个数、排序、配编号、两两配对,全靠它们。
| 函数 | 干什么 | 例子 | 结果 |
|---|---|---|---|
len(x) | 数有多少个元素 | len('hello') | 5 |
sorted(x) | 排序,返回新列表 | sorted([3, 1, 4, 2]) | [1, 2, 3, 4] |
reversed(x) | 倒过来 | list(reversed([1, 2, 3])) | [3, 2, 1] |
enumerate(x) | 每个元素配上序号 | list(enumerate(['a', 'b'])) | [(0, 'a'), (1, 'b')] |
zip(a, b) | 两串合成一对一对 | list(zip([1, 2], ['a', 'b'])) | [(1, 'a'), (2, 'b')] |
4.1 len():数一数有几个
print(len([1, 2, 3, 4])) # 4
print(len('hello')) # 5
print(len('你好世界')) # 4 中文字也算 1 个
print(len({'a': 1, 'b': 2})) # 2 字典数的是「有几对键值」
4.2 sorted():从小到大排
print(sorted([3, 1, 4, 2])) # [1, 2, 3, 4]
print(sorted([3, 1, 4, 2], reverse=True)) # [4, 3, 2, 1] 从大到小
print(sorted('dcba')) # ['a', 'b', 'c', 'd']
print(sorted((5, 3, 1))) # [1, 3, 5] 元组也能排,还回来的是列表
还可以用 key= 指定「按什么排」:
words = ['banana', 'hi', 'apple']
print(sorted(words, key=len)) # ['hi', 'apple', 'banana'] 按长度
print(sorted([-3, 1, 2], key=abs)) # [1, 2, -3] 按绝对值
sorted() 只是算出一份新的排好序的列表还给你,原来的列表一点没变。要改原来那个列表,得用列表自己的 .sort()——两者的区别见 9.1。4.3 reversed():倒过来
print(list(reversed([1, 2, 3]))) # [3, 2, 1]
print(list(reversed('abc'))) # ['c', 'b', 'a']
reversed() 还回来的是一个「反向迭代器」,不能直接打印或取下标。想看结果要包一层 list():print(reversed([1, 2, 3])) 只会显示一串看不懂的地址。如果只是想把字符串倒过来,第 10 篇里那个切片写法更短:'abc'[::-1]。
4.4 enumerate():一边拿序号,一边拿元素
遍历列表时想知道「这是第几个」,它最省事。
names = ['小满', '阿宝', '小可']
for i, name in enumerate(names):
print(i, name)
# 0 小满
# 1 阿宝
# 2 小可
# 让编号从 1 开始,加 start=1
for i, name in enumerate(names, start=1):
print(i, name)
# 1 小满
# 2 阿宝
# 3 小可
start 的话序号从 0 开始——这是 Python 的习惯(第 10 篇讲索引时也是从 0 开始的)。给人看的编号一般要写 start=1。4.5 zip():像拉链一样合起来
names = ['小满', '阿宝', '小可']
scores = [88, 95, 76]
print(list(zip(names, scores)))
# [('小满', 88), ('阿宝', 95), ('小可', 76)]
# 最常见用法:边配对边取
for name, score in zip(names, scores):
print(name, score)
# 也能一次拉三条
print(list(zip('abc', [1, 2, 3], [True, False, True])))
# [('a', 1, True), ('b', 2, False), ('c', 3, True)]
配上字典,两个列表能立刻变成一张对照表(第 8 篇的字典派上用场了):
print(dict(zip(names, scores)))
# {'小满': 88, '阿宝': 95, '小可': 76}
再记一个「中国味」的用法:把省份和省会一一对上,两行就得到一张对照表:
provinces = ['广东', '湖南', '四川']
capitals = ['广州', '长沙', '成都']
print(dict(zip(provinces, capitals)))
# {'广东': '广州', '湖南': '长沙', '四川': '成都'}
zip() 以最短的那一串为准,多出来的部分直接丢掉:list(zip([1, 2, 3], ['a', 'b'])) 只会得到 [(1, 'a'), (2, 'b')],那个 3 不见了。4.6 顺便认识 any() 和 all()
示例里没提,但做判断时很好用,一起收着:
print(any([0, 0, 1])) # True 只要有「一个」为真就是 True
print(any([0, 0, 0])) # False 全是假才 False
print(all([1, 1, 0])) # False 有一个假就是 False
print(all([1, 1, 1])) # True 全是真才 True
all([]) 是 True、any([]) 是 False。记法:all 要「挑不出毛病」才算通过,一个都没得挑,那就通过;any 要「找出一个」,一个都没找到。len()、max()、sorted()、zip() 主要伺候列表这种「一串东西」,而列表要到第 16 篇才正式开讲。这里先见个面,等列表学完回头看,马上就亲切了。五、高级操作类(4 个)
这一类比前面难一点,看懂了会用就行,不必强求一次记住。它们的共同点是:把「对每个元素做同样的事」这件事,写得比 for 循环更短。
| 函数 | 干什么 | 例子 | 结果 |
|---|---|---|---|
range(a, b, n) | 生成一串整数 | list(range(1, 10, 2)) | [1, 3, 5, 7, 9] |
map(f, x) | 把每个元素都用 f 处理一遍 | list(map(len, ['a', 'bb'])) | [1, 2] |
filter(f, x) | 只留下 f 判为真的元素 | list(filter(str.isdigit, 'a1b2')) | ['1', '2'] |
reduce(f, x) | 把一串「揉」成一个值 | reduce(lambda a, b: a + b, [1, 2, 3]) | 6 |
5.1 range():生成一串整数
print(list(range(5))) # [0, 1, 2, 3, 4]
print(list(range(2, 6))) # [2, 3, 4, 5]
print(list(range(1, 10, 2))) # [1, 3, 5, 7, 9] 第三个参数是「每次加几」
for i in range(3):
print(i) # 0 1 2
range(5) 从 0 数到 4,不包括 5;range(2, 6) 不包括 6。5.2 先认识 lambda:一行的临时小函数
后面三个函数都要「递一个规则进去」,这个规则常写成 lambda。它就是一个没有名字的迷你函数,格式是 lambda 参数: 结果。完整的函数写法在第 22 篇《函数》里讲,这里先做到「看得懂、照着会改」就够了。
# 这两行效果一样:
def double(x): # 正常写法:要起名字,要写 return
return x * 2
double2 = lambda x: x * 2 # lambda 写法:一行搞定,连名字都是临时起的
print(double(3), double2(3)) # 6 6
lambda x: x * 2 时,在心里读成「给一个 x,还你 x 乘 2」就好。冒号后面是结果,不用写 return。5.3 map():每个元素都过一遍同一个处理
nums = [1, 2, 3]
print(list(map(lambda x: x * 2, nums))) # [2, 4, 6] 每个都乘 2
# 递现成的函数进去也行
print(list(map(str, [1, 2, 3]))) # ['1', '2', '3'] 每个都变字符串
print(list(map(len, ['a', 'bb', 'ccc']))) # [1, 2, 3] 每个都量长度
# 也能同时处理两串(一一对应)
print(list(map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6]))) # [5, 7, 9]
5.4 filter():只留下符合条件的
前面那支 lambda 要能判断真假:真就留下,假就丢掉。
nums = [1, 2, 3, 4]
print(list(filter(lambda x: x % 2 == 0, nums))) # [2, 4] 只留偶数
print(list(filter(str.isdigit, 'a1b2c3'))) # ['1', '2', '3'] 只留数字
# 第一个参数写 None,就按「真假」自动筛(第 8 篇《数据类型》里的假值清单)
print(list(filter(None, [0, 1, False, True]))) # [1, True]
5.5 reduce():把一串揉成一个
它不在内置函数里,要先从 functools 里请出来(这是唯一需要 import 的一个)。
from functools import reduce
print(reduce(lambda a, b: a + b, [1, 2, 3, 4])) # 10 等于 ((1+2)+3)+4
print(reduce(lambda a, b: a * b, [1, 2, 3])) # 6 连乘
print(reduce(lambda a, b: a if a > b else b, [5, 3, 7, 2])) # 7 变相的求最大值
map()、filter()、reversed() 还回来的都不是列表,而是「迭代器」——想看结果就包一层 list()。原因见 9.5。另外说实话:
map 和 filter 能做的事,用 for 循环加 if 也都做得到,而且更好读。先能看懂别人的代码,写的时候挑顺手的就行。AI 助手小贴士|key 参数是 sorted 这类函数的灵魂,早一点弄懂后面处处轻松。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:sorted() 的 key 参数是什么意思?用「按绝对值大小排序」和「按最后一个字母排序」两个例子讲清楚
六、字符与编码类(2 个)
这一对函数是「互相反着来」的搭档:一个把字符换成编号,一个把编号换回字符。
| 函数 | 方向 | 例子 | 结果 |
|---|---|---|---|
chr(n) | 编号 → 字符 | chr(65) | 'A' |
ord(s) | 字符 → 编号 | ord('A') | 65 |
这里的「编号」叫 Unicode 码位,就是全世界每个字符在计算机里对应的一个数字。
print(chr(65)) # A
print(chr(97)) # a
print(chr(26417)) # 朱 中文也有编号
print(ord('A')) # 65
print(ord('a')) # 97
print(ord('朱')) # 26417
ord 是 order(顺序、序号),把字符变成序号;chr 是 character(字符),把序号变回字符。两个来回转能还原:chr(ord('朱')) 还是 '朱'。6.1 有什么用
最典型的用法是让字母按编号移动——比如把 'A' 往后挪两位变成 'C':
print(chr(ord('A') + 2)) # C
# 大小写字母的编号正好差 32
print(ord('a') - ord('A')) # 32
print(chr(ord('a') - 32)) # A 小写变大写(手写版,实际用 .upper() 更方便)
也能拿来生成一段字母表:
letters = [chr(i) for i in range(ord('a'), ord('e') + 1)]
print(letters) # ['a', 'b', 'c', 'd', 'e']
ord() 一次只能传一个字符。ord('ab') 会报 TypeError: ord() expected a character, but string of length 2 found;想一次转一串,得用 list(map(ord, 'ab'))。七、输入输出与文件操作(3 个)
这三个前面几篇都见过,这里补上「它们也是内置函数」这个身份,并把文件读写说清楚。
7.1 print():输出
print('Hello, World!') # Hello, World!
print(1 + 2) # 3 括号里可以先算再显示
print('a', 'b', 'c', sep='-') # a-b-c 用 sep 改分隔符
print('第一行', end='') # 结尾默认换行,改掉就不换行
print('接着同一行')
sep 和 end 这两个开关第 6 篇《输入输出》讲得很细,忘了可以回去翻。7.2 input():读键盘,拿到的永远是字符串
name = input('请输入你的名字:')
age = input('请输入你的年龄:')
print(type(name)) # <class 'str'>
print(type(age)) # <class 'str'> 即使你输的是 18,也是文字 "18"
所以要当数字用,必须自己转一次,这就是第 6 篇里那个 int(input(...)):
age = int(input('请输入你的年龄:'))
print(age + 1) # 现在才能做加法
7.3 open():打开文件
它负责打开一个文件,还给你一个文件对象,后面读还是写都由参数决定。
| 模式 | 含义 | 注意 |
|---|---|---|
'r' | 只读(默认) | 文件必须已存在,否则报 FileNotFoundError |
'w' | 写入 | 会清空原内容;文件不存在就新建 |
'a' | 追加 | 在末尾接着写,不清空原内容 |
最稳的写法是配 with,用完自动关文件:
# 读一个文件(假设 score.txt 里每行一个分数)
with open('score.txt', 'r', encoding='utf-8') as f:
lines = f.readlines() # 一次读所有行,得到一个列表
print(lines) # ['95\n', '88\n', '76\n']
# 每行末尾有个换行符,用 strip() 去掉(第 10 篇《字符串》里的字符串方法)
scores = [int(x.strip()) for x in lines]
print(scores) # [95, 88, 76]
print(sum(scores) / len(scores)) # 86.33333333333333
# 写一个文件
with open('result.txt', 'w', encoding='utf-8') as f:
f.write('平均分:' + str(sum(scores) / len(scores)))
encoding='utf-8',否则中文可能变成乱码(这是新手最常撞的墙)。另外注意
'w' 模式会把原文件清空重写,一不小心就把内容弄丢了;只在末尾加东西要用 'a'。f.readlines() 读出来的每一行都带着结尾的换行符,先 strip() 把换行符去掉再转数字最稳妥——int() 其实能自动忽略两端空白,但顺手去掉换行符是好习惯,也方便做别的判断。
课程思政|数据安全与法治素养 · 覆盖之前先备份
打开文件时,'w' 模式会先清空原内容,一不小心就把辛苦整理的资料抹平了,想接着写要用 'a'。现实里的数据也一样,删掉、覆盖掉往往不可逆。动手改之前,先留一份备份,这是对自己负责,也是对数据里涉及的那些人负责。
八、对象信息类(3 个)
这一类不看内容,看的是「这个东西本身」——它是什么类型、是哪一个、能不能算一遍。
| 函数 | 干什么 | 例子 | 结果 |
|---|---|---|---|
type(x) | 看类型 | type(10) | <class 'int'> |
id(x) | 看身份编号 | id(a) | 一串大整数 |
eval(s) | 把字符串当表达式算一遍 | eval('1 + 2') | 3 |
8.1 type():看看这是什么类型
print(type(10)) # <class 'int'>
print(type('hello')) # <class 'str'>
print(type([1, 2, 3])) # <class 'list'>
print(type(3.14)) # <class 'float'>
print(type(True)) # <class 'bool'>
<class 'int'> 这个样子,读的时候只看引号里那个词就行(int)。第 8 篇《基本数据类型》里当「探照灯」用的就是它。8.2 id():看「是不是同一个东西」
每个对象都有一张身份证号,id() 把它拿出来。同一个对象的 id 一定相同,不同对象的 id 一定不同。
a = [1, 2]
b = [1, 2]
c = a
print(a == b) # True 内容一样
print(a is b) # False 但不是同一个东西
print(id(a) == id(b)) # False 身份证号不同
print(c is a) # True c 就是 a 本人
print(id(c) == id(a)) # True
b = a 陷阱」的底层原因:c = a 只是给同一个东西又贴了一张标签,两张标签指的是一个对象(id 相同);而 b = [1, 2] 是另外做了一个新东西(id 不同)。id() 打印出来是一个很大的十进制数(其实是个内存地址)。每次运行程序都会变,不要把它当成固定编号存起来用——它只在「同一次运行、同一个对象」的比较里有意义。8.3 eval():把字符串当表达式算一遍
print(eval('1 + 2')) # 3
print(eval('3 * 4 - 1')) # 11
print(eval('[1, 2, 3]')) # [1, 2, 3] 字符串变成了真正的列表
x = 5
print(eval('x * 2')) # 10 能用前面定义过的变量
eval 适合做「小计算器」:把用户输的一整句算式算出来。但它只能算表达式,不能做赋值——eval('x = 1') 会报 SyntaxError(要执行一整句语句得用 exec())。eval() 会把字符串当成真正的代码来执行。第 6 篇里提过 eval(input(...)) 的写法要谨慎,这里再强调一次:只对你自己写的、或者你完全信任的字符串用 eval(),别人给你的一段文字不要直接喂给它。做类型转换优先用 int() / float()。九、最容易搞混的几组对比
这一节专门收拢「长得像、其实不一样」的几组,都是新手真的会写错的。
9.1 sorted() 和列表的 .sort()
一个是函数,一个是列表自带的方法,行为完全不同:
L = [3, 1, 2]
print(sorted(L)) # [1, 2, 3] 返回一个全新的列表
print(L) # [3, 1, 2] 原来的 L 一点没变
result = L.sort() # 列表自己的方法:原地排序
print(result) # None 它不返回东西!
print(L) # [1, 2, 3] 但 L 自己已经被改成有序的了
| 对比项 | sorted(L) | L.sort() |
|---|---|---|
| 是谁 | 内置函数 | 列表的方法 |
| 要几个参数 | sorted(L)(列表当参数传进去) | L.sort()(点号调用) |
| 返回什么 | 新的排好序的列表 | None |
| 原列表变没变 | 不变 | 变了 |
L.sort() 的返回值接住:写成 new = L.sort(),new 会是 None。原地改的方法都不返回结果,别去接它。9.2 reversed() 和列表的 .reverse()
同样是「函数 vs 方法」的关系,坑法一模一样:
M = [1, 2, 3]
print(list(reversed(M))) # [3, 2, 1] 返回新的,M 没动
print(M) # [1, 2, 3]
r = M.reverse() # 原地倒过来
print(r) # None
print(M) # [3, 2, 1]
ed 的是函数(sorted / reversed),返回新的;不带 ed 的是方法(.sort() / .reverse()),原地改、不给回结果。9.3 round() 的「四舍五入」有时不对
print(round(2.5)) # 2 ← 你以为会是 3
print(round(3.5)) # 4
print(round(0.5)) # 0
print(round(2.675, 2)) # 2.67 ← 你以为会是 2.68
两件事在捣乱:
- 「五入」在 Python 里做的是「取最近的偶数」(数学上叫「银行家舍入」)。所以
2.5变成2、3.5变成4,在 0.5 这个点上永远往偶数那边靠。 - 小数在电脑里存不准(第 8 篇讲过的浮点精度问题)。
2.675在电脑里真实的值其实是2.67499999...,所以被「舍」掉了,得到2.67。
round() 拿来做「显示」很好,拿来做「精确计算」不行。算钱、算成绩这类要求严格的地方,要么先放大成整数算完再缩小(round(2.675 * 100) / 100),要么用专门的 decimal 模块,别指望 round()。另外小数点后的位数只影响显示时,其实可以交给 f 字符串:
f"{3.14159:.2f}" 得到 '3.14'(第 10 篇讲过)。9.4 type() 和 isinstance()
isinstance() 示例里没提,但判断类型时比 type() 更常用,顺便认识一下。
| 对比项 | type(x) == int | isinstance(x, int) |
|---|---|---|
| 问的问题 | 「它就是 int 类型吗」 | 「它算不算 int 这一类」 |
| 子类算不算 | 不算 | 算 |
| 举例 | type(True) == int → False | isinstance(True, int) → True |
| 一次查多种类型 | 要写好几遍 | isinstance(x, (int, float)) |
isinstance(),它更宽松也更符合直觉。为什么 isinstance(True, int) 是 True?因为在 Python 里,真假值本身算整数的一个小分支——这也是 True + 1 等于 2 的原因(第 8 篇提过)。9.5 map() / filter() 返回的不是列表
nums = [1, 2, 3]
print(map(str, nums)) # <map object at 0x...> 看不懂吧
print(list(map(str, nums))) # ['1', '2', '3'] 包一层才行
print(filter(None, [0, 1])) # <filter object at 0x...>
print(list(filter(None, [0, 1]))) # [1]
| 函数 | 返回的是 | 能直接 print 吗 | 能取下标吗 | 怎么办 |
|---|---|---|---|---|
map() | 迭代器 | 不能(显示成一串地址) | 不能 | 包一层 list() |
filter() | 迭代器 | 不能 | 不能 | 包一层 list() |
reversed() | 迭代器 | 不能 | 不能 | 包一层 list() |
range() | range 对象 | 不能 | 能(range(5)[0] 是 0) | 要看内容还是包 list() |
sorted() | 列表 | 能 | 能 | 不用包 |
enumerate() | 枚举对象 | 不能 | 不能 | 包 list() 或直接 for |
TypeError: 'map' object is not subscriptable,或者 'enumerate' object is not subscriptable,就是忘了包 list()。它们是「边用边算」的,不把结果存起来,所以取不了下标。十、三个小例子
把前面学的东西串起来用一遍。每个例子都可以直接复制到 IDLE 里跑(第 3 篇讲过怎么新建文件)。
10.1 例 1:成绩单统计
用到 len、sum、max、min、round、sorted。
scores = [88, 95, 76, 92, 60]
print("人数:", len(scores))
print("总分:", sum(scores))
print("平均分:", round(sum(scores) / len(scores), 1))
print("最高分:", max(scores))
print("最低分:", min(scores))
print("从高到低:", sorted(scores, reverse=True))
运行结果:
人数: 5
总分: 411
平均分: 82.2
最高分: 95
最低分: 60
从高到低: [95, 92, 88, 76, 60]
round(sum(scores) / len(scores), 1) 这一句是从里往外读的:先算总分、再除以人数、最后保留一位小数。第 6 篇里那个「括号从里往外看」的方法在这里一样管用。10.2 例 2:名单编号 + 配成绩
用到 enumerate、zip、dict。
names = ["小满", "阿宝", "小可"]
scores = [88, 95, 76]
# 第一件事:带编号打印名单
for i, name in enumerate(names, start=1):
print(i, name)
# 第二件事:名字和分数一一配对
for name, score in zip(names, scores):
print(name, "考了", score)
# 第三件事:直接拼成一张字典表,以后想查谁的分就查谁
score_table = dict(zip(names, scores))
print(score_table)
print(score_table["阿宝"])
运行结果:
1 小满
2 阿宝
3 小可
小满 考了 88
阿宝 考了 95
小可 考了 76
{'小满': 88, '阿宝': 95, '小可': 76}
95
dict(zip(names, scores)) 这个组合非常常用——两个列表变成一个字典,一行就够。前提是两个列表的顺序是对得上的。10.3 例 3:从一段文字里挑出所有数字
用到 filter、str.isdigit、len、map。
text = "订单A123共4件"
# 挑出所有的数字字符
nums = list(filter(str.isdigit, text))
print("数字字符:", nums) # ['1', '2', '3', '4']
print("一共几个:", len(nums)) # 4
# 再把它们变成真正的整数
numbers = list(map(int, nums))
print("整数列表:", numbers) # [1, 2, 3, 4]
print("加起来:", sum(numbers)) # 10
运行结果:
数字字符: ['1', '2', '3', '4']
一共几个: 4
整数列表: [1, 2, 3, 4]
加起来: 10
filter(str.isdigit, text) 意思是「逐个字符问:你是数字吗?是就留下」。str.isdigit 后面不写括号——它在这里是「把这个本领本身递过去」,而不是现在就调用它。这是新手最容易写错的地方(写了括号会直接报错或得到奇怪结果)。AI 助手小贴士|函数记不完,但要会「见输出想函数」。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:出 5 道填空题:每题给一段代码和预期输出,让我填空该用哪个内置函数,填完对答案
十一、常见问题
下面这些都是按真实报错信息整理的,遇到就先来这儿找。
| 现象 | 原因与处理办法 |
|---|---|
TypeError: object of type 'int' has no len() | len(10) 报的。len() 只能量「一串东西」(字符串、列表、元组、字典、集合),单个数字没有「长度」这种说法。 |
ValueError: invalid literal for int() with base 10: '3.5' | int('3.5') 报的。int() 认不出带小数点的文字,要分两步:int(float('3.5')) 得到 3。 |
TypeError: 'int' object is not iterable | list(10) 报的。转换的前提是「本来就是一串东西」,单个数字转不了列表。 |
TypeError: unsupported operand type(s) for +: 'int' and 'str' | sum([1, '2']) 报的。sum() 只能加数字,字符串要么先 int() 转过来,要么改用字符串拼接。 |
ValueError: max() iterable argument is empty | max([]) 报的。空的东西里挑不出最大值(min([]) 同理)。真要处理可能为空的情况,加 default=:max([], default=0)。 |
TypeError: '<' not supported between instances of 'str' and 'int' | sorted([1, 'a']) 报的。排序要求元素之间能比大小,数字和文字不能比。先统一类型再排。 |
TypeError: 'map' object is not subscriptable | map() 返回的是迭代器,不能取下标,也不能直接打印。包一层 list() 就好(filter、reversed、enumerate 同理)。 |
TypeError: ord() expected a character, but string of length 2 found | ord('ab') 报的。ord() 一次只能转一个字符,想转一串用 list(map(ord, 'ab'))。 |
ValueError: chr() arg not in range(0x110000) | chr() 给的编号超出了 Unicode 的范围(写错数字了),换一个合理的编号。 |
TypeError: unhashable type: 'list' | set([[1], [2]]) 报的。集合的元素必须是「不可变」的,列表能改所以不行;换成元组 set([(1,), (2,)])。 |
TypeError: bad operand type for abs(): 'str' | abs('abc') 报的。abs() 只能处理数字,先确认你手上的是不是数字。 |
FileNotFoundError: [Errno 2] No such file or directory: 'score.txt' | open() 找不到文件。检查三件事:文件名拼对没、文件和程序在不在同一个文件夹、扩展名有没有漏。读文件用 'r' 时文件必须已存在。 |
NameError: name 'abc' is not defined | eval('abc') 报的。本来想算字符串,结果忘了写引号,Python 就去找一个叫 abc 的变量了。检查引号。 |
写好的 sorted(...) 好像「没生效」 | 不是报错,是没接住返回值。sorted() 不改原列表,得写 new = sorted(L);要改原列表直接用 L.sort()(而且它不返回值)。 |
round() 的结果和手工算的不一样 | 正常现象,见 9.3。0.5 的地方会往偶数靠,加上小数本身存不准。round() 只适合拿来显示,精确计算另想办法。 |
int(input(...)) 用户输了个带小数点的数字就崩 | 用户输 18.5 时 int() 会报 ValueError。不确定用户会输整数还是小数时,用 float(input(...))(第 6 篇讲过这一点)。 |
AI 助手小贴士|内置函数几十个,不用背——让 AI 整理成一张速查表,打印出来贴桌边。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:把 len()、max()、min()、sum()、sorted()、abs() 整理成一张表格:功能、参数、一个最简例子,控制在一页内方便打印
十二、随堂自测(10 题)
点击选项即可判分,答错的题会立刻显示解析。答完可以看到正确率和错题清单,也可以重新答一遍。
1关于「内置函数」,下面哪个说法是对的?
- A. 必须先 import 才能用
- B. 不用导入,装好 Python 就能直接用
- C. 必须先自己用 def 定义出来
- D. 只能用在数字上
print()、len() 装好就能用,不用 import、不用 def。要 import 的是模块里的函数(如 math.sqrt);内置函数能处理数字、字符串、列表等各种类型,不只数字。2divmod(10, 3) 返回什么?
- A. (3, 1)
- B. 3
- C. 1
- D. (1, 3)
(3, 1)。写成 (1, 3) 就把顺序记反了。它等价于 (10 // 3, 10 % 3),还能一次拆开:q, r = divmod(10, 3)。3round(3.14159, 2) 返回什么?
- A. 3
- B. 3.1
- C. 3.14
- D. 3.142
3.14。不写第二个参数才是取整(会得到 3)。写 3 则会得到 3.142,别把参数和结果看混了。4用户输入的是文字 "18",想把它变成整数拿来算,该用哪个?
- A. str("18")
- B. float("18")
- C. int("18")
- D. bool("18")
int()。str() 是变成文字,本来就已经是文字了;float() 得到的是 18.0(小数);bool() 得到真假值,非空文字都是 True,跟算数没关系。5list(enumerate(["a", "b"], start=1)) 的结果是?
- A. [("a", 1), ("b", 2)]
- B. [(1, "a"), (2, "b")]
- C. [1, 2]
- D. ["a", "b"]
(编号, 元素)。选项 A 把顺序弄反了。不写 start 时序号从 0 开始,写 start=1 才是从 1 开始。6关于 sorted([3, 1, 2]),下面哪个说法对?
- A. 和列表的 .sort() 完全一样,随便用
- B. sorted() 返回新列表、原列表不变;.sort() 原地排序并返回 None
- C. .sort() 返回排好序的新列表,sorted() 会改原列表
- D. sorted() 只能给字符串排序
ed 的是函数:sorted() 算出一份新列表,原列表一点没变。不带 ed 的是方法:L.sort() 直接改 L 自己,而且不返回东西(返回 None),所以不能写 new = L.sort()。数字、字符串、元组都能排。7下面哪个写法得到的就是列表 [2, 4, 6]?
- A. map(lambda x: x * 2, [1, 2, 3])
- B. list(filter(lambda x: x % 2 == 0, [1, 2, 3]))
- C. filter(lambda x: x * 2, [1, 2, 3])
- D. list(map(lambda x: x * 2, [1, 2, 3]))
list(map(...)) 才是列表 [2, 4, 6]。A 少了 list(),得到的是个 map 对象;C 是 filter 对象;B 虽然是列表,但 filter 会按「非 0 为真」筛选,[1,2,3] 里只留下 2,结果是 [2] 不是 [2,4,6]。8ord("朱") 和 chr(26417) 这两个函数的关系是?
- A. ord 把字符变成编号,chr 把编号变回字符,两者互为反向操作
- B. 两个都是把字符变成编号
- C. chr 把字符变成编号,ord 把编号变回字符
- D. ord 只能用在英文字母上
ord('朱') 是 26417、chr(26417) 又是 '朱'。选项 C 把方向说反了;中文、符号都有编号,不只英文字母。9写 len(10) 会发生什么?
- A. 返回 10
- B. 返回 1
- C. 报 TypeError: object of type 'int' has no len()
- D. 返回 0
len(str(10)))。10写 max([]) 会发生什么?
- A. 返回 0,因为没有最大值
- B. 返回 None
- C. 返回空列表 []
- D. 报 ValueError: max() iterable argument is empty
ValueError 而不是猜一个 0 或 None 给你。如果数据真可能为空,要提前给个兜底:max([], default=0)。这也是为什么打分、比大小的代码要先判断列表是不是空的。