Loading...

文章背景图

🐍 Python 魔法入门:揭秘“数据类型”的七十二变

2026-05-07
14
-
- 分钟
|

写在之前

大家好,我是 MuSen,一个爱分享、带你轻松玩转编程的引路人。无论你是刚接触代码的小白,还是正在准备大厂面试、想要夯实基础的同学,来到这里就对了。

在上一节中,我们聊了“变量”这个贴标签的魔法盒子。今天,我们要深入盒子的内部,看看里面装的到底是什么“货”——也就是 Python 中的数据类型

这一篇,我不仅会教你“怎么用”,更会带你扒开 Python 的源代码,看看底层的“为什么”。这篇文章里融合了无数初学者容易踩坑的细节,并且埋藏了大量大厂面试的高频考点(比如小整数池、Intern机制、深浅拷贝、浮点数精度等)

知识点很多,但我保证,只要跟着我的节奏,那些看似高深的名词都会变得像喝水一样简单。准备好了吗?我们发车!🚀

1.1 数据类型:盒子里到底装了啥?

在 Python 的世界里有一句至理名言:“变量无类型,数据才有类型。” 变量 a 只是一个随时可以换地方贴的标签,而真正分门别类的是内存中的数据对象。Python 里的数据主要分为以下六大门派:

  1. 数值(Number):整数(int)、浮点数(float)、复数(complex)、布尔(bool)

  2. 字符串(String):比如 "Hello MuSen"

  3. 列表(List):用 [] 装起来的一排数据(今天的新朋友!)

  4. 元组(Tuple):用 () 装起来的不可变数据

  5. 集合(Set):用 {} 装起来的去重神器

  6. 字典(Dictionary):带有键值对的 {key: value}

  7. 特殊类型(None):表示“四大皆空”、啥也没有。

💡 MuSen 的面试必考科普:可变与不可变 面试官常问:“Python 中哪些是可变类型,哪些是不可变类型?为什么?”

  • 不可变数据(铁公鸡,改不了):数值、字符串、元组。修改它们的值,本质上是重新在内存中开辟了一块新空间,然后把变量标签贴过去,原地址的数据不能变。正因为不可变,它们才能作为字典的 Key(可哈希)。

  • 可变数据(橡皮泥,随便捏):列表、字典、集合。你可以在原有的内存地址上直接修改里面的内容(比如追加元素),内存地址 id() 不会变。

1.2 数值家族:整型、浮点型与布尔值

1.2.1 整型 (int) 与四则运算的魔法 🧮

Python 处理整数的能力极其彪悍!与其他编程语言(如 C/Java 需要区分 int, long, short)不同,Python 的整型只有一种,它可以处理任意大小的整数,包括负整数。 不管你输入的数字多大,只要你电脑的内存管够,它就算飞到了天际也跑不出 Python 的五指山,永远不会出现溢出(Overflow)的问题

除了熟悉的 +-*,Python 里的进阶运算大有学问:

  • / (普通除法):无论能不能除尽,结果永远是浮点数!哪怕是 4 / 2,结果也是 2.0

  • // (整除/向下取整):注意,它是向下取整10 // 33;但 -10 // 3 结果是 -4(面试坑点⚠️)。

  • % (取模/余数)5 % 2 结果是 1

  • (指数):算次方的神器。2 3 结果是 8

🧠 面试超硬核专区:小整数池与大整数池 面试官:“解释一下 a = 256; b = 256; print(a is b)a = 257; b = 257; print(a is b) 的结果?” MuSen 答疑: 为了提升性能,避免频繁申请和销毁内存空间,Python 解释器在启动时偷偷建了一个小整数池(范围是 [-5, 256])。在这个范围内的整数都是提前在内存中造好的,全局共享。 所以如果是 256a is bTrue。如果是 257,超出了范围,底层会为 a 和 b 分别开辟新的内存,所以 a is bFalse(注:在 PyCharm 等 IDE 中运行由于编译器的常量折叠优化,257 也可能返回 True,但原生的命令行交互环境必定是 False!答出这个细节,面试官直接给你疯狂加分!)

1.2.2 浮点型 (float):带着小数点的刺客 🎯

所有带小数点的数都是浮点数。同样,Python 也不区分单精度和双精度,统统都是 float

⚠️ 致命陷阱:精度丢失(经典面试题) 面试官:“为什么 0.1 + 0.2 == 0.3 在 Python 中是 False?” MuSen 答疑:不仅是 Python,几乎所有语言都会这样!因为计算机底层是用二进制的 IEEE 754 标准来存储浮点数的。0.1 和 0.2 转换成二进制是一个无限循环小数,计算机会进行截断,导致最终相加的结果是 0.30000000000000004

🦸‍♂️ 拯救方案:如果涉及金融、交易的精准计算,请请出 decimal 模块!

from decimal import Decimal
f1 = Decimal("0.1")
f2 = Decimal("0.2")
print(f1 + f2 == Decimal("0.3"))  # 输出: True (完美解决!)

1.2.3 布尔型 (bool):非黑即白的裁判 ⚖️

布尔值只有两个:True (真) 和 False (假)。它是 int 的小弟(在底层,boolint 的子类),所以可以参与数学计算:

print(True == 1)      # True
print(True + True)    # 输出 2!惊不惊喜?

除了 False,Python 中还有这些天生为假 (Falsy) 的家伙:None, 0, 0.0, 以及所有的空容器"", [], (), {})。在写 if 判断时,直接写 if not 列表: 判空是最优雅的写法。

1.3 字符串 (str):所见即所得的魔术 📝

字符串是由零个或多个字符组成的序列,用单引号 ''、双引号 "" 或三引号 """ """ 括起来。

1.3.1 字符串的神奇算术:拼接与复制

  • 相加(拼接)'Hello' + ' ' + 'World' 结果是 'Hello World'

  • 相乘(复制):字符串还能做乘法?没错!'Python' * 3 会直接输出 'PythonPythonPython',极其方便!

1.3.2 逃脱大师:转义字符与 r 原生字符串

如果你的字符串里有单引号:'what\'s your name',直接写会报错。需要用反斜杠 \ 转义,或者外层用双引号 "what's your name"。 常见的转义字符有:\n(换行)、\t(Tab)、\r(回车)。

如果你不想让 \ 发生转义(比如写正则或 Windows 路径),在字符串前加个 r,它就会变成原生字符串

print(r'c:\now')  # 输出 c:\now,原汁原味,不把 \n 识别成换行!

1.3.3 切片大师:把字符串大卸八块 🔪

字符串里的每一个字符都有索引(编号)。

  • 正向索引:从 0 开始。

  • 反向索引:从 -1 开始。

切片 (Slicing) 公式:[起始:结束:步长]左闭右开(包含左边,不包含右边)

string = 'I love Python'
print(string[2:6])    # 输出: 'love' 
print(string[7:])     # 输出: 'Python' (一直截取到末尾)
print(string[::-1])   # 经典面试题:一行代码反转字符串!输出: 'nohtyP evol I'

提示:切片对原字符串没有任何影响,它是切出了一个全新的字符串。

1.3.4 必备的字符串方法 (Methods)

Python 给你准备了各种现成的字符串工具,可以通过 dir(str) 查看,千万别自己造轮子!

  • .find('ov'):查找子串,找到返回开始的索引,找不到返回 -1

  • .index('l'):跟 find 一样,但如果找不到,它会直接报错(抛出 ValueError)。

  • .count('i'):统计某个字符在字符串中出现的次数。

  • .replace('旧', '新', 次数):把指定的子串替换成新的,可以指定替换几次。

  • .split(','):根据指定的字符把字符串“劈开”,返回一个列表

  • .strip():一键洗澡,去掉字符串左右两边的所有空白字符(包括空格、换行等)。

1.3.5 字符串格式化(必学 F-string)

传统的 %s.format() 已经老了,现在是 f-string 的天下(Python 3.6+):

name = "MuSen"
age = 18
print(f"大家好,我是 {name},今年 {age} 岁!") # 极其优雅,且运行速度最快!

🧠 面试超硬核专区:字符串的 Intern 机制 (驻留机制) 面试官:“什么是字符串驻留?” MuSen 答疑:Python 为了节省内存,对于只包含字母、数字、下划线的字符串(且在编译期就能确定的),默认开启 Intern 机制,相同的字符串在内存中只保留一份,靠引用计数决定是否销毁!

a = "hello_world"
b = "hello_world"
print(a is b)  # True

1.4 字符编码的前世今生:情报局加密通信 🕵️‍♀️

要讲编码,得讲个故事。长官下令冲锋,小兵吹冲锋号(这就是编码);士兵听见号声,大脑明白要冲锋(这就是解码)。 计算机底层只认识 0 和 1 的高低电平。你要让它懂“你好中国”,就得有一套“密码本”,这就叫字符编码

1.4.1 编码史诗三部曲

  1. ASCII 码:60年代美国人发明的密码本,128个字符,1个字节。只够存英文。

  2. Unicode 码(万国码):像秦始皇统一度量衡,把世界上绝大多数文字都编排进了一套标准里。但在内存中统一占用2个或4个字节,存英文太浪费空间了

  3. UTF-8:互联网的救星。它是 Unicode 的一种实现方式,可变长度编码。存英文只用1个字节,存汉字用3个字节,既兼容又省内存。

1.4.2 字符对应的密码:ord()chr()

因为 Python3 支持 Unicode,每个字都有一个对应的“身份证号”(编码数字):

  • ord('中'):查看字符对应的数字编码(结果:20013)。

  • chr(20013):通过数字编码找回对应的字符(结果:'中')。

1.4.3 Python3 中的 Encode 与 Decode

在 Python3 中,所有在内存中活蹦乱跳的字符串都是 Unicode! 只有当你需要保存到硬盘或通过网络发送时,才需要将其转换为机器能懂的二进制 bytes 类型。

str1 = "你好中国"

# 1. 编码 (Encode):人话(str) -> 机器话(bytes)。默认用 utf-8
byte1 = str1.encode(encoding='utf-8')
print(byte1) # 输出: b'\xe4\xbd\xa0...' (可以看到,UTF-8里一个汉字变成了3个十六进制字节)

# 2. 解码 (Decode):机器话(bytes) -> 人话(str)。
str2 = byte1.decode(encoding='utf-8')

💣 面试踩坑警告:用什么密码本加密,就得用什么密码本解密!如果用 UTF-8 编码却用 GBK 解码,就会出现满屏的“锟斤拷”乱码。

1.5 列表 (List):海纳百川的魔法背包 🎒

如果一个变量只能装一个数据,那太憋屈了。在 Python 中,用方括号 [] 表示的 列表(List) 就是你的神奇背包! 里面可以装数字、字符串,甚至是其他列表。列表里元素的个数理论上是无限大的,只看你电脑的硬件脸色行事。

1.5.1 列表的基本使用与专属方法

列表也是序列,完美继承了字符串的“切片”和“索引”能力,并且它支持倒着数(-1 代表最后一个)。 除了常见的 append(),这里介绍两个极其好用的内置方法:

  • .extend(可迭代对象):合并大师!从另一个列表里获取元素,把它们全部追加到自己的尾巴上,达到扩充的目的。

  • .index(元素):真正的查岗神器。查询某个元素在列表中第一次出现的位置索引

my_list = [10, 20]
my_list.extend([30, 40])  # 把新列表合并进来
print(my_list)            # 输出: [10, 20, 30, 40]
print(my_list.index(30))  # 输出: 2

🧠 面试超硬核专区:列表的时间复杂度 (Big O) 面试官:“在列表头部插入元素和尾部追加元素,哪个快?” MuSen 答疑:尾部快!Python 的列表底层是一个动态数组(一块连续的内存空间)。

  • append()(尾部追加):不需要移动其他元素,时间复杂度是 O(1)

  • insert(0, value)(头部插入):为了给新元素腾位置,后面所有的元素都要集体往后挪一步!时间复杂度是 O(N),数据量大时极其卡顿!

1.5.2 绝杀面试:浅拷贝与深拷贝

这是 Python 面试必考的重点!只要涉及嵌套列表,坑就来了。

import copy
old_list = [1, 2, [3, 4]]

# 1. 赋值 (=):不是拷贝!相当于给同一个箱子多贴了个标签。两人共享一切。
# 2. 浅拷贝 (copy()):只拷贝第一层!
shallow = old_list.copy()
shallow[2][0] = 999 
print(old_list) # 输出: [1, 2, [999, 4]]。原列表的内层也被改了!因为浅拷贝只拷贝了内层列表的引用地址。

# 3. 深拷贝 (deepcopy()):彻彻底底的拷贝,连内层嵌套的列表也重新在内存里挖了一块新空间。
deep = copy.deepcopy(old_list)
deep[2][0] = 888
print(old_list) # 原列表毫发无损!两人彻底分家。

1.6 变身大作战:数据类型转换 🔄

最后,不同数据类型之间是可以换脸的!

1. 隐式转换(自动挡) 当整数遇到浮点数,Python 会自动把“格局”放大,防止数据丢失: type(2 + 3.0) 结果是 <class 'float'>

2. 显式转换(手动挡) 你可以用函数强制转换类型:

  • int("456") -> 转成整数 456

  • float("12.34") -> 转成浮点数 12.34

  • str(123) -> 转成字符串 "123"

💣 面试踩坑警告:警惕 eval() eval() 可以脱去字符串的外衣,直接执行里面的 Python 表达式。 eval("1 + 1") 输出 2。 面试官:“开发中推荐用 eval 解析用户输入吗?” MuSen 答疑:绝对不要!如果黑客输入恶意代码如 eval("os.system('rm -rf /')"),你的服务器就直接被清空删库跑路了!这是一个巨大的安全漏洞。

写在最后

今天我们深挖了各种数据类型的底牌,从小整数池、浮点数精度、字符串的内置方法,一路讲到了字符编码的前世今生、列表的底层效率以及深浅拷贝。

这些不仅是让你写代码不出 Bug 的护身符,更是你日后面试大厂、和面试官谈笑风生的核心资本。干货确实多,但我希望你不要死记硬背!打开你的 IDE 或终端,把代码敲进去跑一跑,真理都在键盘上。

我是 MuSen,我们会点儿技术,有点儿意思,这里是最懂你的地方,咱们下期见!👋

评论交流

文章目录