刚开始学 Python 的人,经常被一行报错弄得不知所措。比如TypeError: can only concatenate str (not "int") to str,或者AttributeError: 'int' object has no attribute 'append'。这些报错看起来千奇百怪,根子往往只有一个:对 Python 数据类型缺乏系统认识。本文就用实际场景把 Python 数据类型完整拆解一遍,从 int、float、str 等基本类型,到 list、dict、tuple、set 等容器类型,再到类型转换、常见误区和实战代码,尽量让你看完后遇到类型相关报错时有清晰的排查思路。
很多初学者会觉得“数据类型”是特别基础的知识点,翻几页文档就能过。但在真实项目中,类型问题恰恰是一周能遇到好几次的坑:别人调用你写的函数时,传入的参数不是预期类型;读取配置文件时,全部是字符串,需要转换成数字和布尔值;爬虫解析出来的数据,要组织成什么结构才能方便后续统计。这些问题并不需要背文档,而是需要建立一套“数据在 Python 中如何组织和变化”的认知框架。读完之后,你会更清楚变量背后的对象到底是什么类型、哪些类型可以修改、哪些类型可以放进字典作为键,以及究竟如何完成安全可靠的数据类型转换。
1. 为什么必须理解 Python 数据类型
1.1 一个报错引发的思考
先看一段最常见的代码:
age = 18 message = "我今年 " + age + " 岁"运行这段代码,会立刻得到一个 TypeError:
TypeError: can only concatenate str (not "int") to str原因是"我今年 "是字符串类型,它只能和字符串拼接,而变量age当前保存的是整数对象18。计算机把"18"和18看成完全不同的两类数据:前者是一个由字符组成的文本序列,后者是一个可以直接参与加减乘除的数值。这个报错提示的就是“可执行的操作”不同,以及 Python 不会自动帮你完成隐式类型转换。
从这段代码能看出,理解数据类型的关键不只是记住有哪些类型,还要清楚每种类型支持哪些操作。字符串支持拼接、切片、查找、替换,数字支持算术运算,列表支持追加元素,字典支持按键取值。如果你希望对一个对象执行它不支持的运算,Python 就会抛出异常。
1.2 Python 类型系统的三个关键词
为了从更高维度理解 Python 数据类型,可以记住三个关键词:动态类型、强类型、一切皆对象。
动态类型意味着变量本身没有固定类型,类型属于对象。同一个变量名可以先保存整数,再被重新赋值为字符串:
x = 100 print(type(x)) # <class 'int'> x = "hello" print(type(x)) # <class 'str'>这种写法在静态类型语言里很难直接做到,但在 Python 里是合法的。好处是灵活、上手快;坏处是代码稍微复杂后,如果不加约束,函数参数到底传入什么类型会变得难以猜测,这也是后面讲类型标注的重要原因。
强类型意味着 Python 不会轻易做隐式类型转换。字符串加整数会报错,数字和字符串比较也会报错。大部分情况下,你必须显式调用int()、str()、float()等方法完成转换。很多从 JavaScript 转过来的同学初期会觉得不习惯,因为 JavaScript 中"1" + 1会自动变成字符串"11",而 Python 会直接告诉你类型错误。
一切皆对象则说明,在 Python 里整数是对象,字符串是对象,函数也是对象。每个对象都有自己的类型,对象的类型决定了它可以被哪些方法处理。正因为如此,我们可以随时使用type()或isinstance()询问这个对象的类型。
2. 环境准备与版本说明
在看数据类型代码时,最好有一台已经安装好 Python 的电脑,或者一个能运行 Python 的在线环境。版本方面,下面所有示例都基于 Python 3 语法,建议使用 3.8 以上版本。这里不写出具体“某一版本”的绝对号召,因为不同操作系统的安装方式有差异,更重要的是掌握type()、isinstance()、int()、list()这些内置函数的使用方式,它们在 Python 3 中表现一致。
你可以先检查自己的环境:
python --versionWindows 下如果没有把 Python 加入 PATH,可能需要使用py -3 --version或直接打开 IDE 内置终端。进入 Python 交互式解释器也很简单:
python在命令行看到类似>>>的提示符后,就可以输入代码并立刻看到输出。比如:
>>> print(type(3)) <class 'int'>我平时更多使用 VS Code 编写.py文件,再配合 Python 插件运行。如果想做数据分析和脚本调试,PyCharm 也是不错的选择。对于本文的简单示例,不需要额外安装第三方库,只用 Python 自带的内置函数即可。
3. 核心基本数据类型详解
3.1 整型、浮点型、复数与布尔值
Python 的整型用int表示,常见的写法就是普通十进制数,比如0、100、-3。与 C 语言不同,Python 3 的整型没有位数上限,理论上可以表示很大的数字。当然,超过一定长度后计算速度会变慢,但不会因为“溢出”直接报错。整型还可以写成二进制、八进制、十六进制形式,例如0b1010表示二进制数字 10,0xFF表示十六进制数字 255。这部分在刷算法题或者做底层协议解析时会用到,平时写业务代码默认十进制即可。
浮点型用float表示,它对应数学中的小数,但计算机内部使用二进制存储小数,因此它并不总能精确表示十进制小数。一个经典例子是:
print(0.1 + 0.2) # 0.30000000000000004这不是 bug,而是浮点数存储机制决定的。如果你在写支付、金额、账本相关代码,千万不要直接使用浮点数做精确计算,更安全的方案是使用Decimal。普通科学计算、数据分析中的绝大多数场景,float的精度已经足够。
复数用complex表示,数学上复数分为实部和虚部,Python 中虚数单位是j:
c = 3 + 4j print(c.real) # 3.0 print(c.imag) # 4.0 print(type(c)) # <class 'complex'>复数主要出现在科学计算、信号处理、数学计算等领域。对于刚入门或主要写 Web、自动化脚本的同学,先知道有这种类型即可,不需要花太多时间。
布尔值bool只有两个取值:True和False。它通常用于条件判断,比如if score >= 60:中的score >= 60会得到一个布尔值。这段表达式成立时返回True,不成立时返回False。从类型体系上看,bool有点特殊,它是int的子类,这意味着True可以当整数 1 使用,False可以当整数 0 使用。写程序时不建议依赖这一点,否则可读性会很差。
3.2 字符串:最常用的文本类型
字符串类型str用来保存文本,例如用户名、日志信息、文件路径、HTML 内容。Python 中定义一个字符串有多种方式:
name = "Alice" city = 'Beijing' info = """ 多行字符串 可以跨行书写 """ path = r"C:\Users\admin\test.txt"单引号和双引号功能基本一致,你可以在包含单引号的文本周围使用双引号,避免转义。三引号适合保存长文本、文档字符串。r开头表示原始字符串,里面的反斜杠不会做转义,Windows 文件路径、正则表达式里经常用它。
字符串是不可变类型。s = "abc"之后,你无法修改s[0]这个字符,只能重新创建新的字符串。这让字符串在作为字典键、集合元素时非常安全。不要混淆“变量重新赋值”和“修改原对象”,例如s = s.upper()是让变量指向一个新的大写字符串,原来的字符串对象并没有变。
索引和切片是处理字符串最常用的手段:
s = "Python" print(s[0]) # P print(s[-1]) # n print(s[0:3]) # Pyt print(s[::-1]) # nohtyP[::-1]这种写法能快速实现字符串反转。实际项目中还会常用以下方法:
text = " hello, python " print(text.strip()) # 去掉首尾空白 print(text.split(",")) # 按逗号拆分成列表 print(text.replace("python", "world")) # 替换 print(text.title()) # Hello, Python如果想拼接多个字符串,直接用+就行,但多个字符串拼接时更推荐join():
parts = ["2025", "01", "01"] print("-".join(parts)) # 2025-01-013.3 空值 None
Python 的None是一个特殊对象,也是NoneType这个类型的唯一实例。它表示“这里什么都没有”或“值暂时未知”。例如用户没有上传头像时,头像字段可以保存为None,表示不存在。定义一个不写return的函数时,函数默认返回None:
def do_nothing(): pass result = do_nothing() print(result) # None判断是否为None,应该用is None,而不是直接用== None。虽然大多数时候二者结果一样,但is是比较对象身份,更符合None的语义。下面的写法才是规范做法:
value = None if value is None: print("值为空")许多新手会把None、False、0、空字符串""、空列表[]混在一起。它们在布尔上下文中都存在“假值”的语义,但代表的意义不同:0是数字中的零,""是长度为 0 的字符串,[]是空列表,None是真正的空对象。如果写if not value:,那么以上情况都会被当成假;如果你只想筛选出没有数据的空值,应该更明确地判断。
4. 容器数据类型:list、tuple、dict、set
4.1 list 列表
列表list是有序、可变的容器,它允许元素重复,也允许不同类型混合存放:
fruits = ["apple", "banana", "pear"] mixed = [1, "two", 3.0, True]列表支持追加、插入、删除和修改:
fruits.append("orange") fruits.insert(1, "grape") fruits[0] = "watermelon" fruits.remove("pear") print(fruits)输出为:
['watermelon', 'grape', 'banana', 'orange']因为列表有序,所以可以像字符串一样使用索引和切片:
nums = [10, 20, 30, 40, 50] print(nums[1:3]) # [20, 30] print(nums[-1]) # 50列表最重要的特性是可变。这意味着多个变量保存同一个列表对象的引用时,修改会互相影响:
a = [1, 2, 3] b = a b.append(4) print(a) # [1, 2, 3, 4]这里a和b指向同一个列表对象,所以通过b修改后,a看到的数据也变了。理解这个现象有助于避免很多副作用问题。
列表推导式是列表常用的高效写法。比如生成 0 到 9 中的偶数列表:
evens = [x for x in range(10) if x % 2 == 0] print(evens) # [0, 2, 4, 6, 8]4.2 tuple 元组
元组tuple与列表一样是有序容器,区别在于元组是不可变对象。普通元组使用小括号:
point = (10, 20) color = "red", "green", "blue" print(point[0]) # 10一个容易踩坑的地方是,定义只有一个元素的元组时,必须写逗号:
t1 = (5) t2 = (5,) print(type(t1)) # <class 'int'> print(type(t2)) # <class 'tuple'>(5)并不会创建元组,它只是括号表达式,会得到整数 5。(5,)才代表包含元素 5 的元组。
元组的不可变指的是“元素指向的对象不能替换”,不是里面的所有内容都不能修改。比如元组中包含一个列表,列表本身仍然可以变:
t = (1, 2, [3, 4]) t[2].append(5) print(t) # (1, 2, [3, 4, 5])正因为元组不可变,它能作为字典的键,而列表不能。函数需要返回多个结果时,元组是很自然的选择,例如返回坐标、状态码和消息。
4.3 dict 字典
字典dict保存的是键值对,每个键对应一个值。字典非常适合表示结构化数据,例如一个学生的姓名和成绩:
student = { "name": "Alice", "score": 88, "courses": ["Python", "Math"], }可以直接修改、添加键值对:
student["age"] = 18 student["score"] = 95 print(student["name"]) # Alice字典的查找速度非常快,即使是很大的字典,按键取值也能在很短时间完成。原理上,字典利用键的哈希值定位存储位置,所以一个对象能否作为键,取决于它是否可哈希。不可变对象通常可哈希,比如字符串、数字、元组;可变对象通常不可哈希,所以列表、字典、集合都不能作为字典键。
需要特别注意,直接使用student["age"]时,如果键不存在,会抛出KeyError。为了避免异常,可以使用get方法并返回一个默认值:
age = student.get("age", 0) print(age)遍历字典也有多种方式:
for key in student: print(key) for value in student.values(): print(value) for key, value in student.items(): print(key, value)Python 3.7 开始,普通字典会保持键的插入顺序,也就是说你按什么顺序放进去,遍历时基本就是什么顺序。这一点在很多业务场景中很有用,但不要依赖在旧版本上的行为。
4.4 set 集合
集合set是一个无序且元素唯一的容器。它最常用的场景是去重和集合判断:
words = ["apple", "banana", "apple", "orange"] unique_words = set(words) print(unique_words)输出可能为:
{'banana', 'apple', 'orange'}集合不会保证输出顺序,而且你可以看到重复的"apple"只剩一个。数值型集合还支持交集、并集、差集等运算:
a = {1, 2, 3} b = {2, 3, 4} print(a & b) # 交集:{2, 3} print(a | b) # 并集:{1, 2, 3, 4} print(a - b) # 差集:{1}因为集合是无序的,所以它不支持通过下标访问。如果需要可哈希的不可变集合,可以使用frozenset,例如frozenset({1, 2, 3}),它可以作为字典键或嵌套到另一个集合里。
4.5 可变与不可变对比
| 类型 | 是否可变 | 是否可哈希 | 典型使用场景 |
|---|---|---|---|
| int | 不可变 | 是 | 数值运算 |
| float | 不可变 | 是 | 科学计算、小数 |
| str | 不可变 | 是 | 文本、日志、标识 |
| tuple | 不可变 | 元素均可哈希时可哈希 | 固定结构数据 |
| list | 可变 | 否 | 动态数组、队列 |
| dict | 可变 | 否 | 键值映射 |
| set | 可变 | 否 | 去重、集合判断 |
| frozenset | 不可变 | 是 | 需要哈希的集合 |
这张表在面试和笔试中经常出现,核心要记住:可变对象通常不能作为字典键,也不能放进集合;不可变对象在跨函数传递时,不容易被函数内部意外修改。实际项目中,如果你希望一个数据能传给函数并保持不变,优先考虑元组或字符串这类不可变类型。
5. 类型转换与类型判断
5.1 常用转换函数
Python 提供了一批内置函数,用于在不同数据类型之间转换。最常见的包括int()、float()、str()、bool()、list()、tuple()、set()、dict()。
从字符串转换成数字时,字符串内容必须是合法的数字文本:
print(int("42")) # 42 print(float("3.14")) # 3.14把字符串"3.14"直接转成整数会失败,因为里面含有小数点:
int("3.14")运行结果:
ValueError: invalid literal for int() with base 10: '3.14'正确做法是先转成浮点数,再转整数:
print(int(float("3.14"))) # 3数值之间的转换则更直接:
print(int(3.99)) # 3 print(float(10)) # 10.0需要注意的是,int(3.99)会向零方向截断小数部分,而不是四舍五入。如果你想要四舍五入,应该使用round(3.99),或者借助Decimal控制精确舍入规则。布尔值的转换也比较容易理解:非零数字转为True,零转为False:
print(bool(0)) # False print(bool(-1)) # True print(bool("")) # False print(bool("a")) # True容器之间互相转换常用于数据处理。例如把字符串转成列表、把列表转成元组、把列表转成集合去重:
s = "hello" print(list(s)) # ['h', 'e', 'l', 'l', 'o'] print(tuple([1, 2, 3])) # (1, 2, 3) print(set([1, 2, 2, 3])) # {1, 2, 3}5.2 转换误区与安全建议
初学者最容易犯的典型错误是混淆“字符串内容”和“显示形式”。比如用户输入age = input("请输入年龄:"),得到的age永远是字符串。如果直接执行age + 1,会立刻报错,必须先int(age)再参与运算。
还有一个特别容易出错的例子:
flag = bool("False") print(flag) # True字符串"False"并不是空字符串,所以在布尔转换时结果为True。只有空字符串""才会转换为False。如果想根据字符串内容"False"得到布尔值False,需要自己判断:
text = "False" flag = text == "True" print(flag) # False另外,不建议使用eval()随意把字符串执行成对象。很多教程里会出现eval("3 + 5"),看起来能计算字符串表达式,但如果你接收的是用户输入,eval("__import__('os').system('...')")这类构造能让攻击者执行任意代码,安全风险非常高。转换数字用int()或float(),解析布尔值用显式判断即可。
5.3 type 与 isinstance 的区别
动态类型语言中经常需要判断数据类型,有人习惯用type(),有人习惯用isinstance()。两者都能判断对象的类型,但推荐优先使用isinstance(),因为它在面对继承时结果更符合直觉。
看一个例子:
class Animal: pass class Dog(Animal): pass dog = Dog() print(type(dog) == Animal) # False print(isinstance(dog, Animal)) # Truetype(dog) == Animal会判断 dog 的类型是否严格等于 Animal,因为 dog 的类型是 Dog,所以结果为 False。isinstance(dog, Animal)则会判断 dog 是不是 Animal 或 Animal 子类的实例,结果自然为 True。在 Python 中,这种子类关系很常见,因此条件判断时用isinstance更合理。
布尔值也有一个特殊现象:
print(type(True) == int) # False print(isinstance(True, int)) # True因为bool是int的子类,所以True是int的实例,但type(True)并不是int。如果业务上必须严格区分整数和布尔值,需要结合type判断,否则按正常逻辑使用isinstance即可。
6. 实战案例:学生成绩统计程序
6.1 需求与数据结构设计
前面讲了很多概念,这里用一个完整的小项目把它们串起来。需求很简单:输入若干学生的姓名和成绩,最后统计平均分、最高分、最低分,以及不及格学生名单。
在设计数据结构时,最合适的选择是字典,因为学生姓名和成绩天然是键值对关系:
students = { "Alice": 92, "Bob": 57, "Cindy": 76, }姓名作为键,成绩作为值。这样后续取某个学生成绩、统计最高分、按分数筛选都很方便。输入过程可能包含非法格式,因此还要对字符串进行拆分和转换并处理ValueError。
6.2 代码实现
将完整代码写到score_statistics.py:
def input_students(): students = {} while True: line = input("请输入姓名和成绩,用空格分隔,输入 end 结束:") if line.strip().lower() == "end": break parts = line.split() if len(parts) != 2: print("格式错误,请输入:姓名 成绩") continue name, score_str = parts try: score = float(score_str) except ValueError: print("成绩必须是数字") continue students[name] = score return students def calculate_statistics(students): if not students: return None, None, None, [] scores = list(students.values()) avg_score = sum(scores) / len(scores) max_score = max(scores) min_score = min(scores) failed_students = [ name for name, score in students.items() if score < 60 ] return avg_score, max_score, min_score, failed_students if __name__ == "__main__": student_dict = input_students() print("学生数据:", student_dict) avg_score, max_score, min_score, failed_students = calculate_statistics(student_dict) if avg_score is None: print("没有录入任何学生数据") else: print(f"平均分:{avg_score:.2f}") print(f"最高分:{max_score:.2f}") print(f"最低分:{min_score:.2f}") if failed_students: print("不及格名单:", failed_students) else: print("本次没有不及格学生")这段代码使用了前面提到的几种数据类型:字典保存学生信息,list(students.values())把字典的值转换为列表,sum()和max()接受可迭代对象,列表推导式筛选不及格学生。函数返回多个结果时,Python 会打包成一个元组,接受时可以用多个变量解包。
6.3 运行与结果说明
在终端运行:
python score_statistics.py输入示例:
请输入姓名和成绩,用空格分隔,输入 end 结束:Alice 92 请输入姓名和成绩,用空格分隔,输入 end 结束:Bob 57 请输入姓名和成绩,用空格分隔,输入 end 结束:Cindy 76 请输入姓名和成绩,用空格分隔,输入 end 结束:David 88 请输入姓名和成绩,用空格分隔,输入 end 结束:end预期输出:
学生数据: {'Alice': 92.0, 'Bob': 57.0, 'Cindy': 76.0, 'David': 88.0} 平均分:78.25 最高分:92.00 最低分:57.00 不及格名单: ['Bob']为什么成绩会从整数变成浮点数?因为代码中使用float(score_str)把输入文本转换成了浮点类型。如果希望保留整数,可以使用int(score_str),但如果用户输入88.5就会报错。实际项目中,需要根据业务对成绩精度的要求选择合适的数据类型。分数通常需要小数,所以浮点数是合理选择。
这个案例虽然不大,却覆盖了本文的核心思路:先判断业务数据适合用什么容器,再设计输入转换逻辑,最后利用列表推导式、元组解包等特性完成统计。如果你能独立把这个流程写出来,说明你对 Python 数据类型已经有了实际使用能力。
7. 常见问题与排查思路
类型问题在真实开发中非常高频,下面把几个最典型的现象、原因和解决思路整理成表。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
TypeError: can only concatenate str (not "int") to str | 字符串和数字直接拼接 | 先把数字转成字符串:str(age) |
ValueError: invalid literal for int() with base 10: 'abc' | 字符串内容不是数字 | 转换前先校验字符串格式,或用正则表达过滤 |
TypeError: 'int' object is not iterable | 对整数使用 for 循环或list() | 确认需要遍历的是列表、元组、字典等可迭代对象 |
TypeError: unhashable type: 'list' | 把列表作为字典键或放入集合 | 改用元组,或重新设计数据结构 |
KeyError: 'xxx' | 字典中不存在对应键 | 使用get(key, default)安全取值 |
AttributeError: 'NoneType' object has no attribute 'xxx' | 函数返回 None,但继续调用方法 | 先判断是否为 None,再执行后续操作 |
list默认参数导致数据串用 | 函数定义时使用可变对象作为默认值 | 默认值写None,函数内部重新创建列表 |
看最容易被忽视的默认参数问题。不要这样写:
def add_item(item, container=[]): container.append(item) return container第一次调用add_item(1)返回[1],第二次调用add_item(2)返回[1, 2]。原因是默认参数container=[]只在函数定义时创建一次,多次调用共用同一个列表对象。安全写法是:
def add_item(item, container=None): if container is None: container = [] container.append(item) return container排查这类问题时,可以先查看报错栈里最后一行提示的是哪个操作不支持,再向上一层找到变量是哪里来的。动态类型语言里,最常见的排查手段就是打印type(variable)和variable的值,确认它到底是不是你预想的类型。养成这个习惯后,大多数类型问题都能在本机快速定位。
8. 最佳实践与工程建议
数据类型是代码的地基。地基不稳,上层再漂亮的业务逻辑都会不断出 bug。我在实际项目中总结了几条经验。
第一,善用类型标注。Python 的优势是动态灵活,但大型项目中完全靠肉眼推测类型会提高沟通成本。给函数参数和返回值加上类型标注,可以让静态检查工具帮助你提前发现错误。比如成绩统计函数可以写成:
from typing import Dict, List, Tuple, Optional def calculate_statistics( students: Dict[str, float] ) -> Tuple[Optional[float], Optional[float], Optional[float], List[str]]: if not students: return None, None, None, [] scores = list(students.values()) avg_score = sum(scores) / len(scores) max_score = max(scores) min_score = min(scores) failed_students = [name for name, score in students.items() if score < 60] return avg_score, max_score, min_score, failed_students类型标注不会影响程序运行,但能让 PyCharm、mypy、Pylance 等工具正确推断类型,也能让其他人一眼看出函数应该传入什么数据。
第二,判断 None 使用is None,判断容器内容使用==。字符串比较也使用==,而不是is。is比较的是对象身份,虽然 Python 对小整数和短字符串有一些缓存机制,偶尔a is b会返回 True,但这并不是可依赖的语言保证。规范写法能避免很多玄学问题。
第三,理解赋值与复制的区别。list_b = list_a只是把引用复制了一份,两个变量指向同一个列表。如果希望得到一个独立的副本,可以使用切片list_a[:]、list(list_a)或copy.copy()浅拷贝。嵌套列表时浅拷贝仍然共享内层列表,需要深拷贝时再考虑copy.deepcopy()。
第四,拼接字符串不要过分依赖+。虽然+写起来直观,但在循环中反复拼接长字符串会不断创建新字符串,影响性能。只需要把每一段文本放到列表中,然后用"".join(parts)一次完成。
第五,不要用可变对象作为函数默认参数。这个原因前面已经说了,它属于高频 bug 来源之一,一旦业务复杂后很难排查,因为每次调用的结果都取决于之前调用过程留下的状态。
第六,对用户输入保持警惕。从input()、网络接口、文件读取得到的数据默认都是字符串,甚至可能是空字符串、带空格的内容。在转换成数字之前,先用strip()去掉首尾空白再做合法性校验。需要计算或解析表达式时,不要对不可信输入使用eval(),否则等于把代码执行入口暴露给外部。
第七,选择合适的数据结构是“磨刀不误砍柴工”。需要频繁按键取值时用字典;需要保持顺序且频繁追加时用列表;固定不变的结构用元组;去重用集合。数据结构选得对,代码会更短且更少出错。
9. 总结与下一步学习方向
本文从底层类型系统讲起,介绍了 Python 中 int、float、bool、str、None 等基础类型,也分析了 list、tuple、dict、set 四种常见容器的特性。理解可变与不可变是学习 Python 数据类型的关键分水岭:不可变对象更安全,可以作为字典键;可变对象更灵活,但要注意共享引用和默认参数带来的副作用。
在实际开发里,你不需要把所有方法都背下来,但应该知道每个容器适合什么业务结构,看到异常时能根据TypeError、ValueError、KeyError等关键词快速定位到数据和操作之间是否匹配。类型判断优先用isinstance,输入转换前先想清楚字符串格式,这些习惯能帮你省下大量调试时间。
数据类型只是 Python 的基础模块。接下来建议按这个顺序继续学习:先温习分支、循环和函数定义,再尝试读写文件,把字符串、字典、列表组合起来完成一个小的数据统计任务。等熟练之后,可以进入面向对象编程,学习类与self、属性与方法、继承和多态。如果对数据分析感兴趣,早期接触 pandas 时也要先弄清楚 DataFrame 中每个列的对象类型,因为那背后本质上还是本文讲的这些 Python 基本类型在发挥作用。
可以试试不看代码,自己完成一个“学生成绩统计”的简化版本:不断输入姓名和成绩,按end结束,打印平均分和最高分,并筛选出成绩低于 60 分的同学。能独立写出来,说明这一篇的内容已经真正变成你自己的技能了。遇到报错不用紧张,打印变量、确认类型、再决定如何转换,三步走基本能解决绝大多数问题。